From 0834d5637517a967d21208299aeceaf5d9dc8cb7 Mon Sep 17 00:00:00 2001 From: Richard LT Date: Wed, 22 Apr 2020 15:02:18 +0200 Subject: [PATCH] feat(api): lock update ascode with template, workflow pull with template (#5134) --- engine/api/environment/environment.go | 4 +- engine/api/pipeline/pipeline.go | 2 +- engine/api/workflow.go | 17 +------ engine/api/workflow/dao.go | 18 ++++++- engine/api/workflow/workflow_exporter.go | 18 +++++-- engine/api/workflow/workflow_parser.go | 4 +- engine/api/workflow_ascode.go | 12 +++-- engine/api/workflow_import.go | 16 +++--- engine/api/workflow_run.go | 6 +-- engine/api/workflowtemplate/aggregate.go | 39 -------------- engine/api/workflowtemplate/aggregate_test.go | 51 ------------------- engine/repositories/processor.go | 6 +-- engine/repositories/processor_common.go | 8 +-- sdk/error.go | 17 ++++--- sdk/workflow.go | 1 - .../authentication/logout.interceptor.ts | 18 ++++--- .../workflow-template.param-form.html | 6 ++- 17 files changed, 86 insertions(+), 157 deletions(-) delete mode 100644 engine/api/workflowtemplate/aggregate.go delete mode 100644 engine/api/workflowtemplate/aggregate_test.go diff --git a/engine/api/environment/environment.go b/engine/api/environment/environment.go index 6f55561e89..c3af4735cc 100644 --- a/engine/api/environment/environment.go +++ b/engine/api/environment/environment.go @@ -78,7 +78,7 @@ func LoadEnvironmentByID(db gorp.SqlExecutor, ID int64) (*sdk.Environment, error WHERE id = $1` if err := db.QueryRow(query, ID).Scan(&env.ID, &env.Name, &env.ProjectID, &env.FromRepository); err != nil { if err == sql.ErrNoRows { - return nil, sdk.ErrEnvironmentNotFound + return nil, sdk.WithStack(sdk.ErrEnvironmentNotFound) } return nil, err } @@ -99,7 +99,7 @@ func LoadEnvironmentByName(db gorp.SqlExecutor, projectKey, envName string) (*sd var lastModified time.Time if err := db.QueryRow(query, projectKey, envName).Scan(&env.ID, &env.Name, &env.ProjectID, &env.FromRepository, &lastModified); err != nil { if err == sql.ErrNoRows { - return nil, sdk.ErrorWithData(sdk.ErrEnvironmentNotFound, envName) + return nil, sdk.WithData(sdk.ErrEnvironmentNotFound, envName) } return nil, sdk.WithStack(err) } diff --git a/engine/api/pipeline/pipeline.go b/engine/api/pipeline/pipeline.go index fdd8f57351..1b86074f34 100644 --- a/engine/api/pipeline/pipeline.go +++ b/engine/api/pipeline/pipeline.go @@ -31,7 +31,7 @@ func LoadPipeline(ctx context.Context, db gorp.SqlExecutor, projectKey, name str if err := db.SelectOne(&p, query, name, projectKey); err != nil { if err == sql.ErrNoRows { - return nil, sdk.WithStack(sdk.ErrorWithData(sdk.ErrPipelineNotFound, name)) + return nil, sdk.WithData(sdk.ErrPipelineNotFound, name) } return nil, sdk.WithStack(err) } diff --git a/engine/api/workflow.go b/engine/api/workflow.go index 547a58735b..39fb591a16 100644 --- a/engine/api/workflow.go +++ b/engine/api/workflow.go @@ -21,7 +21,6 @@ import ( "github.com/ovh/cds/engine/api/project" "github.com/ovh/cds/engine/api/services" "github.com/ovh/cds/engine/api/workflow" - "github.com/ovh/cds/engine/api/workflowtemplate" "github.com/ovh/cds/engine/service" "github.com/ovh/cds/sdk" "github.com/ovh/cds/sdk/exportentities" @@ -86,6 +85,7 @@ func (api *API) getWorkflowHandler() service.Handler { WithLabels: withLabels, WithAsCodeUpdateEvent: withAsCodeEvents, WithIntegrations: true, + WithTemplate: withTemplate, } w1, err := workflow.Load(ctx, api.mustDB(), api.Cache, *proj, name, opts) if err != nil { @@ -113,21 +113,6 @@ func (api *API) getWorkflowHandler() service.Handler { w1.Audits = audits } - if withTemplate { - if err := workflowtemplate.AggregateTemplateInstanceOnWorkflow(ctx, api.mustDB(), w1); err != nil { - return err - } - if w1.TemplateInstance != nil { - if err := workflowtemplate.LoadInstanceOptions.WithTemplate(ctx, api.mustDB(), w1.TemplateInstance); err != nil { - return err - } - if w1.TemplateInstance.Template != nil { - w1.FromTemplate = fmt.Sprintf("%s@%d", w1.TemplateInstance.Template.Path(), w1.TemplateInstance.WorkflowTemplateVersion) - w1.TemplateUpToDate = w1.TemplateInstance.Template.Version == w1.TemplateInstance.WorkflowTemplateVersion - } - } - } - if isAdmin(ctx) { w1.Permissions = sdk.Permissions{Readable: true, Writable: true, Executable: true} } else { diff --git a/engine/api/workflow/dao.go b/engine/api/workflow/dao.go index 17affc78f8..7c108384da 100644 --- a/engine/api/workflow/dao.go +++ b/engine/api/workflow/dao.go @@ -24,6 +24,7 @@ import ( "github.com/ovh/cds/engine/api/keys" "github.com/ovh/cds/engine/api/observability" "github.com/ovh/cds/engine/api/pipeline" + "github.com/ovh/cds/engine/api/workflowtemplate" "github.com/ovh/cds/sdk" "github.com/ovh/cds/sdk/log" ) @@ -71,6 +72,7 @@ type LoadOptions struct { WithIcon bool WithAsCodeUpdateEvent bool WithIntegrations bool + WithTemplate bool } // UpdateOptions is option to parse a workflow @@ -587,6 +589,18 @@ func load(ctx context.Context, db gorp.SqlExecutor, proj sdk.Project, opts LoadO res.EventIntegrations = integrations } + if opts.WithTemplate { + wti, err := workflowtemplate.LoadInstanceByWorkflowID(ctx, db, res.ID, workflowtemplate.LoadInstanceOptions.WithTemplate) + if err != nil && !sdk.ErrorIs(err, sdk.ErrNotFound) { + return nil, err + } + if wti != nil { + res.TemplateInstance = wti + res.FromTemplate = fmt.Sprintf("%s@%d", wti.Template.Path(), wti.WorkflowTemplateVersion) + res.TemplateUpToDate = wti.Template.Version == wti.WorkflowTemplateVersion + } + } + _, next = observability.Span(ctx, "workflow.load.loadNotifications") notifs, errN := loadNotifications(db, &res) next() @@ -1256,7 +1270,7 @@ func checkProjectIntegration(proj sdk.Project, w *sdk.Workflow, n *sdk.Node) err } } if ppProj.ID == 0 { - return sdk.WithStack(sdk.ErrorWithData(sdk.ErrIntegrationtNotFound, n.Context.ProjectIntegrationName)) + return sdk.WithData(sdk.ErrIntegrationtNotFound, n.Context.ProjectIntegrationName) } w.ProjectIntegrations[ppProj.ID] = ppProj n.Context.ProjectIntegrationID = ppProj.ID @@ -1338,7 +1352,7 @@ func checkApplication(store cache.Store, db gorp.SqlExecutor, proj sdk.Project, appDB, err := application.LoadByName(db, proj.Key, n.Context.ApplicationName, application.LoadOptions.WithDeploymentStrategies, application.LoadOptions.WithVariables) if err != nil { if sdk.ErrorIs(err, sdk.ErrNotFound) { - return sdk.WithStack(sdk.ErrorWithData(sdk.ErrNotFound, n.Context.ApplicationName)) + return sdk.WithData(sdk.ErrNotFound, n.Context.ApplicationName) } return sdk.WrapError(err, "unable to load application %s", n.Context.ApplicationName) } diff --git a/engine/api/workflow/workflow_exporter.go b/engine/api/workflow/workflow_exporter.go index 651b154a64..d3e14e2a08 100644 --- a/engine/api/workflow/workflow_exporter.go +++ b/engine/api/workflow/workflow_exporter.go @@ -2,6 +2,7 @@ package workflow import ( "context" + "fmt" "github.com/go-gorp/gorp" @@ -19,9 +20,9 @@ func Export(ctx context.Context, db gorp.SqlExecutor, cache cache.Store, proj sd ctx, end := observability.Span(ctx, "workflow.Export") defer end() - wf, errload := Load(ctx, db, cache, proj, name, LoadOptions{}) - if errload != nil { - return v2.Workflow{}, sdk.WrapError(errload, "workflow.Export> Cannot load workflow %s", name) + wf, err := Load(ctx, db, cache, proj, name, LoadOptions{}) + if err != nil { + return v2.Workflow{}, sdk.WrapError(err, "cannot load workflow %s", name) } // If repo is from as-code do not export WorkflowSkipIfOnlyOneRepoWebhook @@ -48,11 +49,22 @@ func Pull(ctx context.Context, db gorp.SqlExecutor, cache cache.Store, proj sdk. wf, err := Load(ctx, db, cache, proj, name, LoadOptions{ DeepPipeline: true, + WithTemplate: true, }) if err != nil { return wp, sdk.WrapError(err, "cannot load workflow %s", name) } + if wf.TemplateInstance != nil { + return exportentities.WorkflowComponents{ + Template: exportentities.TemplateInstance{ + Name: wf.Name, + From: fmt.Sprintf("%s@%d", wf.TemplateInstance.Template.Path(), wf.TemplateInstance.WorkflowTemplateVersion), + Parameters: wf.TemplateInstance.Request.Parameters, + }, + }, nil + } + // Reload app to retrieve secrets for i := range wf.Applications { app := wf.Applications[i] diff --git a/engine/api/workflow/workflow_parser.go b/engine/api/workflow/workflow_parser.go index 9bbd9a9f27..75cd33b02c 100644 --- a/engine/api/workflow/workflow_parser.go +++ b/engine/api/workflow/workflow_parser.go @@ -69,9 +69,9 @@ func ParseAndImport(ctx context.Context, db gorp.SqlExecutor, store cache.Store, // Get spawn infos from error msg, ok := sdk.ErrorToMessage(err) if ok { - return nil, []sdk.Message{msg}, sdk.WrapError(err, "Workflow is not valid") + return nil, []sdk.Message{msg}, sdk.WrapError(err, "workflow is not valid") } - return nil, nil, sdk.WrapError(err, "Workflow is not valid") + return nil, nil, sdk.WrapError(err, "workflow is not valid") } if err := RenameNode(ctx, db, w); err != nil { diff --git a/engine/api/workflow_ascode.go b/engine/api/workflow_ascode.go index 7cc5ebd93e..668c5537dd 100644 --- a/engine/api/workflow_ascode.go +++ b/engine/api/workflow_ascode.go @@ -37,14 +37,13 @@ func (api *API) getWorkflowAsCodeHandler() service.Handler { } } -// postWorkflowAsCodeHandler Update an as code workflow -// @title Make the workflow as code -// @title Update an as code workflow +// postWorkflowAsCodeHandler update an ascode workflow, this will create a pull request to target repository. func (api *API) postWorkflowAsCodeHandler() service.Handler { return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { vars := mux.Vars(r) key := vars["key"] workflowName := vars["permWorkflowName"] + migrate := FormBool(r, "migrate") branch := FormString(r, "branch") message := FormString(r, "message") @@ -64,6 +63,7 @@ func (api *API) postWorkflowAsCodeHandler() service.Handler { wfDB, err := workflow.Load(ctx, api.mustDB(), api.Cache, *p, workflowName, workflow.LoadOptions{ DeepPipeline: migrate, WithAsCodeUpdateEvent: migrate, + WithTemplate: true, }) if err != nil { return err @@ -84,7 +84,11 @@ func (api *API) postWorkflowAsCodeHandler() service.Handler { // UPDATE EXISTING AS CODE WORKFLOW if wfDB.FromRepository == "" { - return sdk.WithStack(sdk.ErrForbidden) + return sdk.NewErrorFrom(sdk.ErrForbidden, "cannot update a workflow that is not ascode") + } + + if wfDB.TemplateInstance != nil { + return sdk.NewErrorFrom(sdk.ErrForbidden, "cannot update a workflow that was generated by a template") } // Get workflow from body diff --git a/engine/api/workflow_import.go b/engine/api/workflow_import.go index 58ccba5f40..c4d572b7ce 100644 --- a/engine/api/workflow_import.go +++ b/engine/api/workflow_import.go @@ -217,25 +217,23 @@ func (api *API) putWorkflowImportHandler() service.Handler { // if workflow is as-code, we can't save it from edit as yml if wf.FromRepository != "" { - return sdk.WithStack(sdk.ErrForbidden) + return sdk.NewErrorFrom(sdk.ErrForbidden, "can't edit a workflow that is ascode") } - tx, errtx := api.mustDB().Begin() - if errtx != nil { - return sdk.WrapError(errtx, "Unable to start transaction") + tx, err := api.mustDB().Begin() + if err != nil { + return sdk.WrapError(err, "unable to start transaction") } - defer func() { - _ = tx.Rollback() - }() + defer tx.Rollback() //nolint wrkflw, msgList, globalError := workflow.ParseAndImport(ctx, tx, api.Cache, *proj, wf, ew, u, workflow.ImportOptions{Force: true, WorkflowName: wfName}) msgListString := translate(r, msgList) if globalError != nil { if len(msgListString) != 0 { sdkErr := sdk.ExtractHTTPError(globalError, r.Header.Get("Accept-Language")) - return service.WriteJSON(w, append(msgListString, sdkErr.Message), sdkErr.Status) + return service.WriteJSON(w, append(msgListString, sdkErr.Error()), sdkErr.Status) } - return sdk.WrapError(globalError, "Unable to import workflow %s", ew.GetName()) + return sdk.WrapError(globalError, "unable to import workflow %s", ew.GetName()) } if err := tx.Commit(); err != nil { diff --git a/engine/api/workflow_run.go b/engine/api/workflow_run.go index 31b607d429..f1d1fc063d 100644 --- a/engine/api/workflow_run.go +++ b/engine/api/workflow_run.go @@ -23,7 +23,6 @@ import ( "github.com/ovh/cds/engine/api/permission" "github.com/ovh/cds/engine/api/project" "github.com/ovh/cds/engine/api/workflow" - "github.com/ovh/cds/engine/api/workflowtemplate" "github.com/ovh/cds/engine/service" "github.com/ovh/cds/sdk" "github.com/ovh/cds/sdk/log" @@ -894,15 +893,12 @@ func (api *API) postWorkflowRunHandler() service.Handler { WithAsCodeUpdateEvent: true, WithIcon: true, WithIntegrations: true, + WithTemplate: true, }) if errWf != nil { return sdk.WrapError(errWf, "unable to load workflow %s", name) } - if err := workflowtemplate.AggregateTemplateInstanceOnWorkflow(ctx, api.mustDB(), wf); err != nil { - return sdk.WrapError(err, "cannot load workflow template") - } - // Check node permission if isService := isService(ctx); !isService && !permission.AccessToWorkflowNode(ctx, api.mustDB(), wf, &wf.WorkflowData.Node, getAPIConsumer(ctx), sdk.PermissionReadExecute) { return sdk.WrapError(sdk.ErrNoPermExecution, "not enough right on node %s", wf.WorkflowData.Node.Name) diff --git a/engine/api/workflowtemplate/aggregate.go b/engine/api/workflowtemplate/aggregate.go deleted file mode 100644 index 4ab7397b72..0000000000 --- a/engine/api/workflowtemplate/aggregate.go +++ /dev/null @@ -1,39 +0,0 @@ -package workflowtemplate - -import ( - "context" - - "github.com/go-gorp/gorp" - - "github.com/ovh/cds/sdk" -) - -// AggregateTemplateInstanceOnWorkflow set template instance data for each workflow. -func AggregateTemplateInstanceOnWorkflow(ctx context.Context, db gorp.SqlExecutor, ws ...*sdk.Workflow) error { - if len(ws) == 0 { - return nil - } - - wtis, err := LoadInstancesByWorkflowIDs(ctx, db, sdk.WorkflowToIDs(ws)) - if err != nil { - return err - } - if len(wtis) == 0 { - return nil - } - - mWorkflowTemplateInstances := make(map[int64]sdk.WorkflowTemplateInstance, len(wtis)) - for _, wti := range wtis { - if wti.WorkflowID != nil { - mWorkflowTemplateInstances[*wti.WorkflowID] = wti - } - } - - for _, w := range ws { - if wti, ok := mWorkflowTemplateInstances[w.ID]; ok { - w.TemplateInstance = &wti - } - } - - return nil -} diff --git a/engine/api/workflowtemplate/aggregate_test.go b/engine/api/workflowtemplate/aggregate_test.go deleted file mode 100644 index eab25fd810..0000000000 --- a/engine/api/workflowtemplate/aggregate_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package workflowtemplate - -import ( - "context" - "fmt" - "reflect" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/ovh/cds/engine/api/test" - "github.com/ovh/cds/sdk" -) - -func TestAggregateTemplateInstanceOnWorkflow(t *testing.T) { - db := &test.SqlExecutorMock{} - - ids := []int64{4, 5, 6} - db.OnSelect = func(i interface{}) { - fmt.Println(reflect.TypeOf(i)) - if wtis, ok := i.(*[]*sdk.WorkflowTemplateInstance); ok { - *wtis = append(*wtis, - &sdk.WorkflowTemplateInstance{ - WorkflowTemplateID: 1, - WorkflowID: &ids[0], - }, - &sdk.WorkflowTemplateInstance{ - WorkflowTemplateID: 1, - WorkflowID: &ids[1], - }, - &sdk.WorkflowTemplateInstance{ - WorkflowTemplateID: 2, - WorkflowID: &ids[2], - }) - } - } - - ws := []*sdk.Workflow{{ID: 4}, {ID: 5}, {ID: 6}} - - assert.Nil(t, AggregateTemplateInstanceOnWorkflow(context.TODO(), db, ws...)) - - require.NotNil(t, ws[0].TemplateInstance) - assert.Equal(t, int64(1), ws[0].TemplateInstance.WorkflowTemplateID) - - require.NotNil(t, ws[1].TemplateInstance) - assert.Equal(t, int64(1), ws[1].TemplateInstance.WorkflowTemplateID) - - require.NotNil(t, ws[2].TemplateInstance) - assert.Equal(t, int64(2), ws[2].TemplateInstance.WorkflowTemplateID) -} diff --git a/engine/repositories/processor.go b/engine/repositories/processor.go index d5eada9c4f..ef0a0f0c66 100644 --- a/engine/repositories/processor.go +++ b/engine/repositories/processor.go @@ -55,7 +55,7 @@ func (s *Service) do(ctx context.Context, op sdk.Operation) error { } log.ErrorWithFields(ctx, fields, "%s", err) - op.Error = sdk.Cause(err).Error() + op.Error = sdk.ExtractHTTPError(err, "").Error() op.Status = sdk.OperationStatusError } else { op.Error = "" @@ -70,7 +70,7 @@ func (s *Service) do(ctx context.Context, op sdk.Operation) error { } log.ErrorWithFields(ctx, fields, "%s", err) - op.Error = sdk.Cause(err).Error() + op.Error = sdk.ExtractHTTPError(err, "").Error() op.Status = sdk.OperationStatusError } else { op.Error = "" @@ -91,7 +91,7 @@ func (s *Service) do(ctx context.Context, op sdk.Operation) error { } log.ErrorWithFields(ctx, fields, "%s", err) - op.Error = sdk.Cause(err).Error() + op.Error = sdk.ExtractHTTPError(err, "").Error() op.Status = sdk.OperationStatusError } else { op.Error = "" diff --git a/engine/repositories/processor_common.go b/engine/repositories/processor_common.go index 4f3818e67a..2d2ebba5bf 100644 --- a/engine/repositories/processor_common.go +++ b/engine/repositories/processor_common.go @@ -33,18 +33,18 @@ func (s *Service) processGitClone(ctx context.Context, op *sdk.Operation) (repo. log.Info(ctx, "processGitClone> cloning %s into %s", r.URL, r.Basedir) gitRepo, err = repo.Clone(r.Basedir, r.URL, opts...) if err != nil { - return gitRepo, "", "", err + return gitRepo, "", "", sdk.NewErrorFrom(err, "cannot clone repository at given url: %s", r.URL) } } f, err := gitRepo.FetchURL() if err != nil { - return gitRepo, "", "", err + return gitRepo, "", "", sdk.WithStack(err) } d, err := gitRepo.DefaultBranch() if err != nil { - return gitRepo, "", "", err + return gitRepo, "", "", sdk.WithStack(err) } op.RepositoryInfo = &sdk.OperationRepositoryInfo{ @@ -56,7 +56,7 @@ func (s *Service) processGitClone(ctx context.Context, op *sdk.Operation) (repo. //Check branch currentBranch, err := gitRepo.CurrentBranch() if err != nil { - return gitRepo, "", "", err + return gitRepo, "", "", sdk.WithStack(err) } return gitRepo, r.Basedir, currentBranch, nil } diff --git a/sdk/error.go b/sdk/error.go index ea0d6b2a38..cc5a4502cb 100644 --- a/sdk/error.go +++ b/sdk/error.go @@ -374,8 +374,8 @@ var errorsAmericanEnglish = map[int]string{ ErrInvalidPassword.ID: "Your value of type password isn't correct", ErrRepositoryUsedByHook.ID: "There is still a repository webhook on this repository", ErrResourceNotInProject.ID: "The resource is not attached to the project", - ErrEnvironmentNotFound.ID: "environment not found", - ErrIntegrationtNotFound.ID: "integration not found", + ErrEnvironmentNotFound.ID: "Environment not found ", + ErrIntegrationtNotFound.ID: "Integration not found", ErrSignupDisabled.ID: "Sign up is disabled for target consumer type", ErrBadBrokerConfiguration.ID: "Cannot connect to the broker of your event integration. Check your configuration", ErrInvalidJobRequirementNetworkAccess.ID: "Invalid job requirement: network requirement must contains ':'. Example: golang.org:http, golang.org:443", @@ -556,7 +556,8 @@ var errorsFrench = map[int]string{ ErrInvalidPassword.ID: "Votre valeur de type mot de passe n'est pas correct", ErrRepositoryUsedByHook.ID: "Il y a encore un repository webhook sur ce dépôt", ErrResourceNotInProject.ID: "La ressource n'est pas lié au projet", - ErrEnvironmentNotFound.ID: "l'environnement n'existe pas", + ErrEnvironmentNotFound.ID: "L'environnement n'existe pas", + ErrIntegrationtNotFound.ID: "L'intégration n'existe pas", ErrSignupDisabled.ID: "La création de compte est désactivée pour ce mode d'authentification.", ErrBadBrokerConfiguration.ID: "Impossible de se connecter à votre intégration de type évènement. Veuillez vérifier votre configuration", ErrInvalidJobRequirementNetworkAccess.ID: "Pré-requis de job invalide: Le pré-requis network doit contenir un ':'. Exemple: golang.org:http, golang.org:443", @@ -821,10 +822,12 @@ func WithStack(err error) error { } } -// ErrorWithData returns an error with data from given error. -func ErrorWithData(err Error, data interface{}) error { - err.Data = data - return err +// WithData returns an error with data from given error. +func WithData(err error, data interface{}) error { + err = WithStack(err) + withStack := err.(errorWithStack) + withStack.httpError.Data = data + return withStack } // ExtractHTTPError tries to recognize given error and return http error diff --git a/sdk/workflow.go b/sdk/workflow.go index 85c860623c..3049fa8ee4 100644 --- a/sdk/workflow.go +++ b/sdk/workflow.go @@ -53,7 +53,6 @@ type Workflow struct { EventIntegrations []ProjectIntegration `json:"event_integrations,omitempty" db:"-" cli:"-"` AsCodeEvent []AsCodeEvent `json:"as_code_events,omitempty" db:"-" cli:"-"` // aggregates - Template *WorkflowTemplate `json:"-" db:"-" cli:"-"` TemplateInstance *WorkflowTemplateInstance `json:"-" db:"-" cli:"-"` FromTemplate string `json:"from_template,omitempty" db:"-" cli:"-"` TemplateUpToDate bool `json:"template_up_to_date,omitempty" db:"-" cli:"-"` diff --git a/ui/src/app/service/authentication/logout.interceptor.ts b/ui/src/app/service/authentication/logout.interceptor.ts index be66389aa2..ef877013d5 100644 --- a/ui/src/app/service/authentication/logout.interceptor.ts +++ b/ui/src/app/service/authentication/logout.interceptor.ts @@ -33,13 +33,19 @@ export class LogoutInterceptor implements HttpInterceptor { this._router.navigate(['/auth/signin'], navigationExtras); } else if (req.url.indexOf('auth/me') === -1) { // ignore error on auth/me used for auth pages + console.log(e) // error formatted from CDS API - if (e.error && e.error.message) { - this._toast.error(e.statusText, e.error.message); - } else { - try { - this._toast.error(e.statusText, JSON.parse(e.message)); - } catch (e) { + if (e.error) { + if (e.error.message) { + this._toast.error(e.statusText, e.error.message); + } else if (Array.isArray(e.error)) { + try { + let messages = e.error as Array; + this._toast.error(e.statusText, messages.join(', ')); + } catch (e) { + this._toast.error(e.statusText, this._translate.instant('common_error')); + } + } else { this._toast.error(e.statusText, this._translate.instant('common_error')); } } diff --git a/ui/src/app/shared/workflow-template/param-form/workflow-template.param-form.html b/ui/src/app/shared/workflow-template/param-form/workflow-template.param-form.html index c6cddf7775..bac68301f2 100644 --- a/ui/src/app/shared/workflow-template/param-form/workflow-template.param-form.html +++ b/ui/src/app/shared/workflow-template/param-form/workflow-template.param-form.html @@ -28,13 +28,15 @@ + (selectedOptionChange)="changeParam()" isSearchable="true" + [(ngModel)]="parameterValues[parameter.key]" #select> + (selectedOptionChange)="changeParam()" isSearchable="true" + [(ngModel)]="parameterValues[parameter.key]" #select>