feat: mark coder_parameter as "required" (#6433)

* Add required column

* Pass through providerd

* Pass the required property down

* Optional

* Fix

* Fix

* Fix

* fix

* CLI create: support for optional fields

* Use HTML API to mark fields required

* Fix

* Improve validation

* more fixes

* make fmt

* Fix

* WIP

* Fix: test

* CLI update tets

* OptionalParameterAdded

* Fix: migration
This commit is contained in:
Marcin Tojek
2023-03-07 16:38:31 +01:00
committed by GitHub
parent f19076cf06
commit 2d4706ac33
30 changed files with 651 additions and 409 deletions

3
coderd/apidoc/docs.go generated
View File

@ -7966,6 +7966,9 @@ const docTemplate = `{
"$ref": "#/definitions/codersdk.TemplateVersionParameterOption"
}
},
"required": {
"type": "boolean"
},
"type": {
"type": "string",
"enum": [

View File

@ -7170,6 +7170,9 @@
"$ref": "#/definitions/codersdk.TemplateVersionParameterOption"
}
},
"required": {
"type": "boolean"
},
"type": {
"type": "string",
"enum": ["string", "number", "bool"]

View File

@ -2722,6 +2722,7 @@ func (q *fakeQuerier) InsertTemplateVersionParameter(_ context.Context, arg data
ValidationMin: arg.ValidationMin,
ValidationMax: arg.ValidationMax,
ValidationMonotonic: arg.ValidationMonotonic,
Required: arg.Required,
}
q.templateVersionParameters = append(q.templateVersionParameters, param)
return param, nil

View File

@ -351,6 +351,7 @@ CREATE TABLE template_version_parameters (
validation_max integer NOT NULL,
validation_error text DEFAULT ''::text NOT NULL,
validation_monotonic text DEFAULT ''::text NOT NULL,
required boolean DEFAULT true NOT NULL,
CONSTRAINT validation_monotonic_order CHECK ((validation_monotonic = ANY (ARRAY['increasing'::text, 'decreasing'::text, ''::text])))
);
@ -378,6 +379,8 @@ COMMENT ON COLUMN template_version_parameters.validation_error IS 'Validation: e
COMMENT ON COLUMN template_version_parameters.validation_monotonic IS 'Validation: consecutive values preserve the monotonic order';
COMMENT ON COLUMN template_version_parameters.required IS 'Is parameter required?';
CREATE TABLE template_version_variables (
template_version_id uuid NOT NULL,
name text NOT NULL,

View File

@ -0,0 +1 @@
ALTER TABLE template_version_parameters DROP COLUMN required;

View File

@ -0,0 +1,3 @@
ALTER TABLE template_version_parameters ADD COLUMN required boolean NOT NULL DEFAULT true; -- default: true, as so far every parameter should be marked as required
COMMENT ON COLUMN template_version_parameters.required IS 'Is parameter required?';

View File

@ -1467,6 +1467,8 @@ type TemplateVersionParameter struct {
ValidationError string `db:"validation_error" json:"validation_error"`
// Validation: consecutive values preserve the monotonic order
ValidationMonotonic string `db:"validation_monotonic" json:"validation_monotonic"`
// Is parameter required?
Required bool `db:"required" json:"required"`
}
type TemplateVersionVariable struct {

View File

@ -3556,7 +3556,7 @@ func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateT
}
const getTemplateVersionParameters = `-- name: GetTemplateVersionParameters :many
SELECT template_version_id, name, description, type, mutable, default_value, icon, options, validation_regex, validation_min, validation_max, validation_error, validation_monotonic FROM template_version_parameters WHERE template_version_id = $1
SELECT template_version_id, name, description, type, mutable, default_value, icon, options, validation_regex, validation_min, validation_max, validation_error, validation_monotonic, required FROM template_version_parameters WHERE template_version_id = $1
`
func (q *sqlQuerier) GetTemplateVersionParameters(ctx context.Context, templateVersionID uuid.UUID) ([]TemplateVersionParameter, error) {
@ -3582,6 +3582,7 @@ func (q *sqlQuerier) GetTemplateVersionParameters(ctx context.Context, templateV
&i.ValidationMax,
&i.ValidationError,
&i.ValidationMonotonic,
&i.Required,
); err != nil {
return nil, err
}
@ -3611,7 +3612,8 @@ INSERT INTO
validation_min,
validation_max,
validation_error,
validation_monotonic
validation_monotonic,
required
)
VALUES
(
@ -3627,8 +3629,9 @@ VALUES
$10,
$11,
$12,
$13
) RETURNING template_version_id, name, description, type, mutable, default_value, icon, options, validation_regex, validation_min, validation_max, validation_error, validation_monotonic
$13,
$14
) RETURNING template_version_id, name, description, type, mutable, default_value, icon, options, validation_regex, validation_min, validation_max, validation_error, validation_monotonic, required
`
type InsertTemplateVersionParameterParams struct {
@ -3645,6 +3648,7 @@ type InsertTemplateVersionParameterParams struct {
ValidationMax int32 `db:"validation_max" json:"validation_max"`
ValidationError string `db:"validation_error" json:"validation_error"`
ValidationMonotonic string `db:"validation_monotonic" json:"validation_monotonic"`
Required bool `db:"required" json:"required"`
}
func (q *sqlQuerier) InsertTemplateVersionParameter(ctx context.Context, arg InsertTemplateVersionParameterParams) (TemplateVersionParameter, error) {
@ -3662,6 +3666,7 @@ func (q *sqlQuerier) InsertTemplateVersionParameter(ctx context.Context, arg Ins
arg.ValidationMax,
arg.ValidationError,
arg.ValidationMonotonic,
arg.Required,
)
var i TemplateVersionParameter
err := row.Scan(
@ -3678,6 +3683,7 @@ func (q *sqlQuerier) InsertTemplateVersionParameter(ctx context.Context, arg Ins
&i.ValidationMax,
&i.ValidationError,
&i.ValidationMonotonic,
&i.Required,
)
return i, err
}

View File

@ -13,7 +13,8 @@ INSERT INTO
validation_min,
validation_max,
validation_error,
validation_monotonic
validation_monotonic,
required
)
VALUES
(
@ -29,7 +30,8 @@ VALUES
$10,
$11,
$12,
$13
$13,
$14
) RETURNING *;
-- name: GetTemplateVersionParameters :many

View File

@ -828,6 +828,7 @@ func (server *Server) CompleteJob(ctx context.Context, completed *proto.Complete
ValidationMin: richParameter.ValidationMin,
ValidationMax: richParameter.ValidationMax,
ValidationMonotonic: richParameter.ValidationMonotonic,
Required: richParameter.Required,
})
if err != nil {
return nil, xerrors.Errorf("insert parameter: %w", err)

View File

@ -1621,6 +1621,7 @@ func convertTemplateVersionParameter(param database.TemplateVersionParameter) (c
ValidationMax: param.ValidationMax,
ValidationError: param.ValidationError,
ValidationMonotonic: codersdk.ValidationMonotonicOrder(param.ValidationMonotonic),
Required: param.Required,
}, nil
}

View File

@ -1884,3 +1884,87 @@ func TestWorkspaceWithRichParameters(t *testing.T) {
require.NoError(t, err)
require.ElementsMatch(t, expectedBuildParameters, workspaceBuildParameters)
}
func TestWorkspaceWithOptionalRichParameters(t *testing.T) {
t.Parallel()
const (
firstParameterName = "first_parameter"
firstParameterType = "string"
firstParameterDescription = "This is _first_ *parameter*"
firstParameterDefaultValue = "1"
secondParameterName = "second_parameter"
secondParameterType = "number"
secondParameterDescription = "_This_ is second *parameter*"
secondParameterRequired = true
secondParameterValue = "333"
)
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
Parse: echo.ParseComplete,
ProvisionPlan: []*proto.Provision_Response{
{
Type: &proto.Provision_Response_Complete{
Complete: &proto.Provision_Complete{
Parameters: []*proto.RichParameter{
{
Name: firstParameterName,
Type: firstParameterType,
Description: firstParameterDescription,
DefaultValue: firstParameterDefaultValue,
},
{
Name: secondParameterName,
Type: secondParameterType,
Description: secondParameterDescription,
Required: secondParameterRequired,
},
},
},
},
},
},
ProvisionApply: []*proto.Provision_Response{{
Type: &proto.Provision_Response_Complete{
Complete: &proto.Provision_Complete{},
},
}},
})
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
templateRichParameters, err := client.TemplateVersionRichParameters(ctx, version.ID)
require.NoError(t, err)
require.Len(t, templateRichParameters, 2)
require.Equal(t, firstParameterName, templateRichParameters[0].Name)
require.Equal(t, firstParameterType, templateRichParameters[0].Type)
require.Equal(t, firstParameterDescription, templateRichParameters[0].Description)
require.Equal(t, firstParameterDefaultValue, templateRichParameters[0].DefaultValue)
require.Equal(t, secondParameterName, templateRichParameters[1].Name)
require.Equal(t, secondParameterType, templateRichParameters[1].Type)
require.Equal(t, secondParameterDescription, templateRichParameters[1].Description)
require.Equal(t, secondParameterRequired, templateRichParameters[1].Required)
expectedBuildParameters := []codersdk.WorkspaceBuildParameter{
// First parameter is optional, so coder will pick the default value.
{Name: secondParameterName, Value: secondParameterValue},
}
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
cwr.RichParameterValues = expectedBuildParameters
})
workspaceBuild := coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
require.Equal(t, codersdk.WorkspaceStatusRunning, workspaceBuild.Status)
workspaceBuildParameters, err := client.WorkspaceBuildParameters(ctx, workspaceBuild.ID)
require.NoError(t, err)
require.ElementsMatch(t, expectedBuildParameters, workspaceBuildParameters)
}