From 33214f585791725c2377fde97e6031c85cccda41 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 13:49:24 +0200 Subject: [PATCH 01/12] Correct backend error --- coderd/apidoc/docs.go | 3 --- coderd/apidoc/swagger.json | 9 +++------ coderd/wsbuilder/wsbuilder.go | 2 +- codersdk/provisionerdaemons.go | 3 +-- docs/api/builds.md | 11 +++++----- docs/api/schemas.md | 14 ++++++------- docs/api/templates.md | 20 +++++++++---------- docs/api/workspaces.md | 10 +++++----- site/src/api/typesGenerated.ts | 9 ++------- .../createTemplate/createTemplateXService.ts | 8 -------- 10 files changed, 32 insertions(+), 57 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 04aed7c9be52a..45c66a1fe2543 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8411,11 +8411,9 @@ const docTemplate = `{ "codersdk.JobErrorCode": { "type": "string", "enum": [ - "MISSING_TEMPLATE_PARAMETER", "REQUIRED_TEMPLATE_VARIABLES" ], "x-enum-varnames": [ - "MissingTemplateParameter", "RequiredTemplateVariables" ] }, @@ -8949,7 +8947,6 @@ const docTemplate = `{ }, "error_code": { "enum": [ - "MISSING_TEMPLATE_PARAMETER", "REQUIRED_TEMPLATE_VARIABLES" ], "allOf": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index ee710a7f8e51f..861f1c878e3dc 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7547,11 +7547,8 @@ }, "codersdk.JobErrorCode": { "type": "string", - "enum": ["MISSING_TEMPLATE_PARAMETER", "REQUIRED_TEMPLATE_VARIABLES"], - "x-enum-varnames": [ - "MissingTemplateParameter", - "RequiredTemplateVariables" - ] + "enum": ["REQUIRED_TEMPLATE_VARIABLES"], + "x-enum-varnames": ["RequiredTemplateVariables"] }, "codersdk.License": { "type": "object", @@ -8045,7 +8042,7 @@ "type": "string" }, "error_code": { - "enum": ["MISSING_TEMPLATE_PARAMETER", "REQUIRED_TEMPLATE_VARIABLES"], + "enum": ["REQUIRED_TEMPLATE_VARIABLES"], "allOf": [ { "$ref": "#/definitions/codersdk.JobErrorCode" diff --git a/coderd/wsbuilder/wsbuilder.go b/coderd/wsbuilder/wsbuilder.go index 3b6bcf1ae832b..d0ae33e90ae95 100644 --- a/coderd/wsbuilder/wsbuilder.go +++ b/coderd/wsbuilder/wsbuilder.go @@ -525,7 +525,7 @@ func (b *Builder) getParameters() (names, values []string, err error) { // At this point, we've queried all the data we need from the database, // so the only errors are problems with the request (missing data, failed // validation, immutable parameters, etc.) - return nil, nil, BuildError{http.StatusBadRequest, err.Error(), err} + return nil, nil, BuildError{http.StatusBadRequest, fmt.Sprintf("failed to validate parameter %q", templateVersionParameter.Name), err} } names = append(names, templateVersionParameter.Name) values = append(values, value) diff --git a/codersdk/provisionerdaemons.go b/codersdk/provisionerdaemons.go index 30e0896707d56..4a3e280697f74 100644 --- a/codersdk/provisionerdaemons.go +++ b/codersdk/provisionerdaemons.go @@ -69,7 +69,6 @@ const ( type JobErrorCode string const ( - MissingTemplateParameter JobErrorCode = "MISSING_TEMPLATE_PARAMETER" RequiredTemplateVariables JobErrorCode = "REQUIRED_TEMPLATE_VARIABLES" ) @@ -81,7 +80,7 @@ type ProvisionerJob struct { CompletedAt *time.Time `json:"completed_at,omitempty" format:"date-time"` CanceledAt *time.Time `json:"canceled_at,omitempty" format:"date-time"` Error string `json:"error,omitempty"` - ErrorCode JobErrorCode `json:"error_code,omitempty" enums:"MISSING_TEMPLATE_PARAMETER,REQUIRED_TEMPLATE_VARIABLES"` + ErrorCode JobErrorCode `json:"error_code,omitempty" enums:"REQUIRED_TEMPLATE_VARIABLES"` Status ProvisionerJobStatus `json:"status" enums:"pending,running,succeeded,canceling,canceled,failed"` WorkerID *uuid.UUID `json:"worker_id,omitempty" format:"uuid"` FileID uuid.UUID `json:"file_id" format:"uuid"` diff --git a/docs/api/builds.md b/docs/api/builds.md index 782e41a6f61aa..f6aa71be7a555 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -39,7 +39,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -201,7 +201,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -759,7 +759,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -926,7 +926,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -1163,7 +1163,6 @@ Status Code **200** | Property | Value | | ------------------------- | ----------------------------- | -| `error_code` | `MISSING_TEMPLATE_PARAMETER` | | `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | | `status` | `pending` | | `status` | `running` | @@ -1273,7 +1272,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, diff --git a/docs/api/schemas.md b/docs/api/schemas.md index a615825d266d3..5052f498afff1 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -3088,7 +3088,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ## codersdk.JobErrorCode ```json -"MISSING_TEMPLATE_PARAMETER" +"REQUIRED_TEMPLATE_VARIABLES" ``` ### Properties @@ -3097,7 +3097,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | Value | | ----------------------------- | -| `MISSING_TEMPLATE_PARAMETER` | | `REQUIRED_TEMPLATE_VARIABLES` | ## codersdk.License @@ -3626,7 +3625,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -3664,7 +3663,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | Property | Value | | ------------ | ----------------------------- | -| `error_code` | `MISSING_TEMPLATE_PARAMETER` | | `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | | `status` | `pending` | | `status` | `running` | @@ -4656,7 +4654,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -5369,7 +5367,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -5976,7 +5974,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -6503,7 +6501,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, diff --git a/docs/api/templates.md b/docs/api/templates.md index 14ce5dec7d29f..407ab84eba439 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -385,7 +385,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -455,7 +455,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -549,7 +549,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -850,7 +850,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -920,7 +920,6 @@ Status Code **200** | Property | Value | | ------------ | ----------------------------- | -| `error_code` | `MISSING_TEMPLATE_PARAMETER` | | `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | | `status` | `pending` | | `status` | `running` | @@ -1024,7 +1023,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions/{templ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -1094,7 +1093,6 @@ Status Code **200** | Property | Value | | ------------ | ----------------------------- | -| `error_code` | `MISSING_TEMPLATE_PARAMETER` | | `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | | `status` | `pending` | | `status` | `running` | @@ -1142,7 +1140,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion} \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -1221,7 +1219,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversion} "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -1347,7 +1345,7 @@ curl -X POST http://coder-server:8080/api/v2/templateversions/{templateversion}/ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -1400,7 +1398,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index f5c4aadd729c5..01b85e21b3527 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -67,7 +67,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -255,7 +255,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -446,7 +446,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -631,7 +631,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, @@ -953,7 +953,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/lock \ "completed_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "error": "string", - "error_code": "MISSING_TEMPLATE_PARAMETER", + "error_code": "REQUIRED_TEMPLATE_VARIABLES", "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "queue_position": 0, diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index e0592b966bca4..0a4526291194f 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1660,13 +1660,8 @@ export type InsightsReportInterval = "day" export const InsightsReportIntervals: InsightsReportInterval[] = ["day"] // From codersdk/provisionerdaemons.go -export type JobErrorCode = - | "MISSING_TEMPLATE_PARAMETER" - | "REQUIRED_TEMPLATE_VARIABLES" -export const JobErrorCodes: JobErrorCode[] = [ - "MISSING_TEMPLATE_PARAMETER", - "REQUIRED_TEMPLATE_VARIABLES", -] +export type JobErrorCode = "REQUIRED_TEMPLATE_VARIABLES" +export const JobErrorCodes: JobErrorCode[] = ["REQUIRED_TEMPLATE_VARIABLES"] // From codersdk/provisionerdaemons.go export type LogLevel = "debug" | "error" | "info" | "trace" | "warn" diff --git a/site/src/xServices/createTemplate/createTemplateXService.ts b/site/src/xServices/createTemplate/createTemplateXService.ts index 3d4205fc23f05..c3c7fe2180e61 100644 --- a/site/src/xServices/createTemplate/createTemplateXService.ts +++ b/site/src/xServices/createTemplate/createTemplateXService.ts @@ -519,7 +519,6 @@ export const createTemplateMachine = hasFailed: (_, { data }) => Boolean( data.job.status === "failed" && - !isMissingParameter(data) && !isMissingVariables(data), ), hasNoParametersOrVariables: (_, { data }) => @@ -531,13 +530,6 @@ export const createTemplateMachine = }, ) -const isMissingParameter = (version: TemplateVersion) => { - return Boolean( - version.job.error_code && - version.job.error_code === "MISSING_TEMPLATE_PARAMETER", - ) -} - const isMissingVariables = (version: TemplateVersion) => { return Boolean( version.job.error_code && From e831d242ec62fba8081b68b69ad5acf50c7f19c6 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 13:52:21 +0200 Subject: [PATCH 02/12] Unable: --- coderd/wsbuilder/wsbuilder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/wsbuilder/wsbuilder.go b/coderd/wsbuilder/wsbuilder.go index d0ae33e90ae95..26090a4f1be99 100644 --- a/coderd/wsbuilder/wsbuilder.go +++ b/coderd/wsbuilder/wsbuilder.go @@ -525,7 +525,7 @@ func (b *Builder) getParameters() (names, values []string, err error) { // At this point, we've queried all the data we need from the database, // so the only errors are problems with the request (missing data, failed // validation, immutable parameters, etc.) - return nil, nil, BuildError{http.StatusBadRequest, fmt.Sprintf("failed to validate parameter %q", templateVersionParameter.Name), err} + return nil, nil, BuildError{http.StatusBadRequest, fmt.Sprintf("Unable to validate parameter %q", templateVersionParameter.Name), err} } names = append(names, templateVersionParameter.Name) values = append(values, value) From e5421c4942d7160687022524e92d130a84d4c829 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 15:23:00 +0200 Subject: [PATCH 03/12] site: ask for changed parameter option --- site/src/api/api.ts | 26 ++++++++++++++++++- .../UpdateBuildParametersDialog.tsx | 3 +++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 4d5c88fe74b16..3742623f57bad 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1220,7 +1220,6 @@ const getMissingParameters = ( if (isMutableAndRequired || isImmutable) { requiredParameters.push(p) - return } }) @@ -1243,6 +1242,31 @@ const getMissingParameters = ( missingParameters.push(parameter) } + // Check if parameter "options" changed and we can't use old build parameters. + templateParameters.forEach((templateParameter) => { + if (templateParameter.options.length === 0) { + return + } + + // Check if there is a new value + let buildParameter = newBuildParameters.find( + (p) => p.name === templateParameter.name, + ) + + // If not, get the old one + if (!buildParameter) { + buildParameter = oldBuildParameters.find((p) => p.name === templateParameter.name) + } + + if (!buildParameter) { + return + } + + const matchingOption = templateParameter.options.find(option => option.value === buildParameter?.value); + if (!matchingOption) { + missingParameters.push(templateParameter) + } + }) return missingParameters } diff --git a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx index 33d42b6bc5ae2..fdd813162ec6d 100644 --- a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx +++ b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx @@ -49,6 +49,9 @@ export const UpdateBuildParametersDialog: FC< onUpdate(values.rich_parameter_values) }, }) + form.values = { + rich_parameter_values: initialRichParameterValues, + } const getFieldHelpers = getFormHelpers(form) const { t } = useTranslation("workspacePage") From 349c6ca1d068e52137f5ebd1ffe9f5bd0ad16bcd Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 15:23:33 +0200 Subject: [PATCH 04/12] wip: do not ask for required immutables on update --- cli/parameterresolver.go | 1 - cli/root.go | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/parameterresolver.go b/cli/parameterresolver.go index f4ed240993538..94516fa4dbd62 100644 --- a/cli/parameterresolver.go +++ b/cli/parameterresolver.go @@ -184,7 +184,6 @@ func (pr *ParameterResolver) resolveWithInput(resolved []codersdk.WorkspaceBuild if (tvp.Ephemeral && pr.promptBuildOptions) || (action == WorkspaceCreate && tvp.Required) || (action == WorkspaceCreate && !tvp.Ephemeral) || - (action == WorkspaceUpdate && tvp.Required) || (action == WorkspaceUpdate && !tvp.Mutable && firstTimeUse) || (action == WorkspaceUpdate && tvp.Mutable && !tvp.Ephemeral && pr.promptRichParameters) { parameterValue, err := cliui.RichParameter(inv, tvp) diff --git a/cli/root.go b/cli/root.go index 2b4db5eb8b221..fad5625d909c0 100644 --- a/cli/root.go +++ b/cli/root.go @@ -981,6 +981,8 @@ func (p *prettyErrorFormatter) format(err error) { msg = sdkError.Message if sdkError.Helper != "" { msg = msg + "\n" + sdkError.Helper + } else if sdkError.Detail != "" { + msg = msg + "\n" + sdkError.Detail } // The SDK error is usually good enough, and we don't want to overwhelm // the user with output. From 0da0b47095f3f2e0785ec7a49ff2dee913843ef3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 15:52:31 +0200 Subject: [PATCH 05/12] test: ParameterOptionChanged --- cli/update_test.go | 61 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/cli/update_test.go b/cli/update_test.go index e799557fcc454..b18b8b03e7734 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -593,12 +593,71 @@ func TestUpdateValidateRichParameters(t *testing.T) { assert.NoError(t, err) }() + pty.ExpectMatch("Planning workspace...") + <-doneChan + }) + + t.Run("ParameterOptionChanged", func(t *testing.T) { + t.Parallel() + + // Create template and workspace + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user := coderdtest.CreateFirstUser(t, client) + + templateParameters := []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "First option", Description: "This is first option", Value: "1st"}, + {Name: "Second option", Description: "This is second option", Value: "2nd"}, + {Name: "Third option", Description: "This is third option", Value: "3rd"}, + }}, + } + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(templateParameters)) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + + inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd")) + clitest.SetupConfig(t, client, root) + err := inv.Run() + require.NoError(t, err) + + // Update template + updatedTemplateParameters := []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "first_option", Description: "This is first option", Value: "1"}, + {Name: "second_option", Description: "This is second option", Value: "2"}, + {Name: "third_option", Description: "This is third option", Value: "3"}, + }}, + } + + updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID) + coderdtest.AwaitTemplateVersionJob(t, client, updatedVersion.ID) + err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ + ID: updatedVersion.ID, + }) + require.NoError(t, err) + + // Update the workspace + inv, root = clitest.New(t, "update", "my-workspace") + clitest.SetupConfig(t, client, root) + doneChan := make(chan struct{}) + pty := ptytest.New(t).Attach(inv) + go func() { + defer close(doneChan) + err := inv.Run() + assert.NoError(t, err) + }() + matches := []string{ - "Planning workspace...", "", + "aaaa", "", } for i := 0; i < len(matches); i += 2 { match := matches[i] + value := matches[i+1] pty.ExpectMatch(match) + + if value != "" { + pty.WriteLine(value) + } } <-doneChan }) From 1c64cfe3ccbbb1fea8aed7b27c83200afaa1809c Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 16:28:36 +0200 Subject: [PATCH 06/12] extra test --- cli/update_test.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/cli/update_test.go b/cli/update_test.go index b18b8b03e7734..48a1d8f7b7c13 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -597,6 +597,73 @@ func TestUpdateValidateRichParameters(t *testing.T) { <-doneChan }) + t.Run("RequiredImmutableParameterAdded", func(t *testing.T) { + t.Parallel() + + // Create template and workspace + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user := coderdtest.CreateFirstUser(t, client) + + templateParameters := []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "First option", Description: "This is first option", Value: "1st"}, + {Name: "Second option", Description: "This is second option", Value: "2nd"}, + {Name: "Third option", Description: "This is third option", Value: "3rd"}, + }}, + } + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(templateParameters)) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + + inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd")) + clitest.SetupConfig(t, client, root) + err := inv.Run() + require.NoError(t, err) + + // Update template: add required, immutable parameter + updatedTemplateParameters := []*proto.RichParameter{ + templateParameters[0], + {Name: immutableParameterName, Type: "string", Mutable: false, Required: true, Options: []*proto.RichParameterOption{ + {Name: "fir", Description: "This is first option for immutable parameter", Value: "I"}, + {Name: "sec", Description: "This is second option for immutable parameter", Value: "II"}, + {Name: "thi", Description: "This is third option for immutable parameter", Value: "III"}, + }}, + } + + updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID) + coderdtest.AwaitTemplateVersionJob(t, client, updatedVersion.ID) + err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ + ID: updatedVersion.ID, + }) + require.NoError(t, err) + + // Update the workspace + inv, root = clitest.New(t, "update", "my-workspace") + clitest.SetupConfig(t, client, root) + doneChan := make(chan struct{}) + pty := ptytest.New(t).Attach(inv) + go func() { + defer close(doneChan) + err := inv.Run() + assert.NoError(t, err) + }() + + matches := []string{ + immutableParameterName, "thi", + "Planning workspace...", "", + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + pty.ExpectMatch(match) + + if value != "" { + pty.WriteLine(value) + } + } + <-doneChan + }) + t.Run("ParameterOptionChanged", func(t *testing.T) { t.Parallel() From 1e41500fdeaaa671e80f69f9d1a4628f50b714a3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 17:25:19 +0200 Subject: [PATCH 07/12] Prompt for changed parameter option --- cli/parameterresolver.go | 29 ++++++++++++++++++++++++++++- cli/update_test.go | 3 ++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/cli/parameterresolver.go b/cli/parameterresolver.go index 94516fa4dbd62..bf0c727e1cd35 100644 --- a/cli/parameterresolver.go +++ b/cli/parameterresolver.go @@ -141,6 +141,10 @@ next: continue // immutables should not be passed to consecutive builds } + if len(tvp.Options) > 0 && !isValidTemplateParameterOption(buildParameter, tvp.Options) { + continue // do not propagate invalid options + } + for i, r := range resolved { if r.Name == buildParameter.Name { resolved[i].Value = buildParameter.Value @@ -180,10 +184,11 @@ func (pr *ParameterResolver) resolveWithInput(resolved []codersdk.WorkspaceBuild // Parameter has not been resolved yet, so CLI needs to determine if user should input it. firstTimeUse := pr.isFirstTimeUse(tvp.Name) - + promptParameterOption := pr.isLastBuildParameterInvalidOption(tvp) if (tvp.Ephemeral && pr.promptBuildOptions) || (action == WorkspaceCreate && tvp.Required) || (action == WorkspaceCreate && !tvp.Ephemeral) || + (action == WorkspaceUpdate && promptParameterOption) || (action == WorkspaceUpdate && !tvp.Mutable && firstTimeUse) || (action == WorkspaceUpdate && tvp.Mutable && !tvp.Ephemeral && pr.promptRichParameters) { parameterValue, err := cliui.RichParameter(inv, tvp) @@ -206,6 +211,19 @@ func (pr *ParameterResolver) isFirstTimeUse(parameterName string) bool { return findWorkspaceBuildParameter(parameterName, pr.lastBuildParameters) == nil } +func (pr *ParameterResolver) isLastBuildParameterInvalidOption(templateVersionParameter codersdk.TemplateVersionParameter) bool { + if len(templateVersionParameter.Options) == 0 { + return false + } + + for _, buildParameter := range pr.lastBuildParameters { + if buildParameter.Name == templateVersionParameter.Name { + return !isValidTemplateParameterOption(buildParameter, templateVersionParameter.Options) + } + } + return false +} + func findTemplateVersionParameter(workspaceBuildParameter codersdk.WorkspaceBuildParameter, templateVersionParameters []codersdk.TemplateVersionParameter) *codersdk.TemplateVersionParameter { for _, tvp := range templateVersionParameters { if tvp.Name == workspaceBuildParameter.Name { @@ -223,3 +241,12 @@ func findWorkspaceBuildParameter(parameterName string, params []codersdk.Workspa } return nil } + +func isValidTemplateParameterOption(buildParameter codersdk.WorkspaceBuildParameter, options []codersdk.TemplateVersionParameterOption) bool { + for _, opt := range options { + if opt.Value == buildParameter.Value { + return true + } + } + return false +} diff --git a/cli/update_test.go b/cli/update_test.go index 48a1d8f7b7c13..a0b1ae70f2e00 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -715,7 +715,8 @@ func TestUpdateValidateRichParameters(t *testing.T) { }() matches := []string{ - "aaaa", "", + stringParameterName, "second_option", + "Planning workspace...", "", } for i := 0; i < len(matches); i += 2 { match := matches[i] From 8cf4b15014b8110638ca5baa4fd5096b3d70b3f3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 17:29:52 +0200 Subject: [PATCH 08/12] cleanup --- site/src/api/api.ts | 8 ++++++-- .../xServices/createTemplate/createTemplateXService.ts | 5 +---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 3742623f57bad..454a8d43e6922 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1255,14 +1255,18 @@ const getMissingParameters = ( // If not, get the old one if (!buildParameter) { - buildParameter = oldBuildParameters.find((p) => p.name === templateParameter.name) + buildParameter = oldBuildParameters.find( + (p) => p.name === templateParameter.name, + ) } if (!buildParameter) { return } - const matchingOption = templateParameter.options.find(option => option.value === buildParameter?.value); + const matchingOption = templateParameter.options.find( + (option) => option.value === buildParameter?.value, + ) if (!matchingOption) { missingParameters.push(templateParameter) } diff --git a/site/src/xServices/createTemplate/createTemplateXService.ts b/site/src/xServices/createTemplate/createTemplateXService.ts index c3c7fe2180e61..bfff04549fe17 100644 --- a/site/src/xServices/createTemplate/createTemplateXService.ts +++ b/site/src/xServices/createTemplate/createTemplateXService.ts @@ -517,10 +517,7 @@ export const createTemplateMachine = isNotUsingExample: ({ exampleId }) => !exampleId, hasFile: ({ file }) => Boolean(file), hasFailed: (_, { data }) => - Boolean( - data.job.status === "failed" && - !isMissingVariables(data), - ), + Boolean(data.job.status === "failed" && !isMissingVariables(data)), hasNoParametersOrVariables: (_, { data }) => data.variables === undefined, hasParametersOrVariables: (_, { data }) => { From 0635b5c3511e103cb84cb984dac5c4b1b813ccea Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 22 Aug 2023 17:30:36 +0200 Subject: [PATCH 09/12] WIP --- site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx index fdd813162ec6d..33d42b6bc5ae2 100644 --- a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx +++ b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx @@ -49,9 +49,6 @@ export const UpdateBuildParametersDialog: FC< onUpdate(values.rich_parameter_values) }, }) - form.values = { - rich_parameter_values: initialRichParameterValues, - } const getFieldHelpers = getFormHelpers(form) const { t } = useTranslation("workspacePage") From e617d16b1fcd97ea5c1c5a0d7f3520f1900523b2 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 23 Aug 2023 13:09:07 +0200 Subject: [PATCH 10/12] After merge --- cli/update_test.go | 67 ---------------------------------------------- 1 file changed, 67 deletions(-) diff --git a/cli/update_test.go b/cli/update_test.go index baa61a587cc99..adb00d22c863e 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -597,73 +597,6 @@ func TestUpdateValidateRichParameters(t *testing.T) { <-doneChan }) - t.Run("RequiredImmutableParameterAdded", func(t *testing.T) { - t.Parallel() - - // Create template and workspace - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - user := coderdtest.CreateFirstUser(t, client) - - templateParameters := []*proto.RichParameter{ - {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ - {Name: "First option", Description: "This is first option", Value: "1st"}, - {Name: "Second option", Description: "This is second option", Value: "2nd"}, - {Name: "Third option", Description: "This is third option", Value: "3rd"}, - }}, - } - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(templateParameters)) - coderdtest.AwaitTemplateVersionJob(t, client, version.ID) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - - inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd")) - clitest.SetupConfig(t, client, root) - err := inv.Run() - require.NoError(t, err) - - // Update template: add required, immutable parameter - updatedTemplateParameters := []*proto.RichParameter{ - templateParameters[0], - {Name: immutableParameterName, Type: "string", Mutable: false, Required: true, Options: []*proto.RichParameterOption{ - {Name: "fir", Description: "This is first option for immutable parameter", Value: "I"}, - {Name: "sec", Description: "This is second option for immutable parameter", Value: "II"}, - {Name: "thi", Description: "This is third option for immutable parameter", Value: "III"}, - }}, - } - - updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID) - coderdtest.AwaitTemplateVersionJob(t, client, updatedVersion.ID) - err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ - ID: updatedVersion.ID, - }) - require.NoError(t, err) - - // Update the workspace - inv, root = clitest.New(t, "update", "my-workspace") - clitest.SetupConfig(t, client, root) - doneChan := make(chan struct{}) - pty := ptytest.New(t).Attach(inv) - go func() { - defer close(doneChan) - err := inv.Run() - assert.NoError(t, err) - }() - - matches := []string{ - immutableParameterName, "thi", - "Planning workspace...", "", - } - for i := 0; i < len(matches); i += 2 { - match := matches[i] - value := matches[i+1] - pty.ExpectMatch(match) - - if value != "" { - pty.WriteLine(value) - } - } - <-doneChan - }) - t.Run("ParameterOptionChanged", func(t *testing.T) { t.Parallel() From 75d0f41249a858be3293d55aceec9e8671e9a7e2 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 23 Aug 2023 14:45:01 +0200 Subject: [PATCH 11/12] use enableReinitialize --- site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx index 33d42b6bc5ae2..860969267fb7e 100644 --- a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx +++ b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx @@ -48,6 +48,7 @@ export const UpdateBuildParametersDialog: FC< onSubmit: (values) => { onUpdate(values.rich_parameter_values) }, + enableReinitialize: true, }) const getFieldHelpers = getFormHelpers(form) const { t } = useTranslation("workspacePage") From c03a58109379a4859c4c0616268531fbd382786d Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 23 Aug 2023 17:08:53 +0200 Subject: [PATCH 12/12] Add test for disappeared option --- cli/parameterresolver.go | 1 + cli/update_test.go | 66 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/cli/parameterresolver.go b/cli/parameterresolver.go index 64a46f0374e0b..486188d52a27a 100644 --- a/cli/parameterresolver.go +++ b/cli/parameterresolver.go @@ -185,6 +185,7 @@ func (pr *ParameterResolver) resolveWithInput(resolved []codersdk.WorkspaceBuild firstTimeUse := pr.isFirstTimeUse(tvp.Name) promptParameterOption := pr.isLastBuildParameterInvalidOption(tvp) + if (tvp.Ephemeral && pr.promptBuildOptions) || (action == WorkspaceCreate && tvp.Required) || (action == WorkspaceCreate && !tvp.Ephemeral) || diff --git a/cli/update_test.go b/cli/update_test.go index adb00d22c863e..57e49d0db3766 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -663,6 +663,72 @@ func TestUpdateValidateRichParameters(t *testing.T) { <-doneChan }) + t.Run("ParameterOptionDisappeared", func(t *testing.T) { + t.Parallel() + + // Create template and workspace + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user := coderdtest.CreateFirstUser(t, client) + + templateParameters := []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "First option", Description: "This is first option", Value: "1st"}, + {Name: "Second option", Description: "This is second option", Value: "2nd"}, + {Name: "Third option", Description: "This is third option", Value: "3rd"}, + }}, + } + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(templateParameters)) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + + inv, root := clitest.New(t, "create", "my-workspace", "--yes", "--template", template.Name, "--parameter", fmt.Sprintf("%s=%s", stringParameterName, "2nd")) + clitest.SetupConfig(t, client, root) + err := inv.Run() + require.NoError(t, err) + + // Update template - 2nd option disappeared, 4th option added + updatedTemplateParameters := []*proto.RichParameter{ + {Name: stringParameterName, Type: "string", Mutable: true, Required: true, Options: []*proto.RichParameterOption{ + {Name: "First option", Description: "This is first option", Value: "1st"}, + {Name: "Third option", Description: "This is third option", Value: "3rd"}, + {Name: "Fourth option", Description: "This is fourth option", Value: "4th"}, + }}, + } + + updatedVersion := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, prepareEchoResponses(updatedTemplateParameters), template.ID) + coderdtest.AwaitTemplateVersionJob(t, client, updatedVersion.ID) + err = client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ + ID: updatedVersion.ID, + }) + require.NoError(t, err) + + // Update the workspace + inv, root = clitest.New(t, "update", "my-workspace") + clitest.SetupConfig(t, client, root) + doneChan := make(chan struct{}) + pty := ptytest.New(t).Attach(inv) + go func() { + defer close(doneChan) + err := inv.Run() + assert.NoError(t, err) + }() + + matches := []string{ + stringParameterName, "Third option", + "Planning workspace...", "", + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + pty.ExpectMatch(match) + + if value != "" { + pty.WriteLine(value) + } + } + <-doneChan + }) + t.Run("ImmutableRequiredParameterExists_MutableRequiredParameterAdded", func(t *testing.T) { t.Parallel() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy