Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf66adac

Browse files
authored
chore: enhance parameter validation error messages (#379)
* chore: implement validation in an exported function* chore: enhance parameter validation error messages
1 parent53a68cd commitf66adac

File tree

3 files changed

+159
-70
lines changed

3 files changed

+159
-70
lines changed

‎provider/formtype.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,11 @@ var formTypeTruthTable = map[OptionType]map[bool][]ParameterFormType{
115115
// The use case is when using multi-select. The options are 'string' and the
116116
// value is 'list(string)'.
117117
funcValidateFormType(paramTypeOptionType,optionCountint,specifiedFormTypeParameterFormType) (OptionType,ParameterFormType,error) {
118-
allowed,ok:=formTypeTruthTable[paramType][optionCount>0]
118+
optionsExist:=optionCount>0
119+
allowed,ok:=formTypeTruthTable[paramType][optionsExist]
119120
if!ok||len(allowed)==0 {
120-
returnparamType,specifiedFormType,xerrors.Errorf("value type %q is not supported for 'form_types'",paramType)
121+
// This error should really never be hit, as the provider sdk does an enum validation.
122+
returnparamType,specifiedFormType,xerrors.Errorf("\"type\" attribute=%q is not supported, choose one of %v",paramType,OptionTypes())
121123
}
122124

123125
ifspecifiedFormType==ParameterFormTypeDefault {
@@ -126,7 +128,27 @@ func ValidateFormType(paramType OptionType, optionCount int, specifiedFormType P
126128
}
127129

128130
if!slices.Contains(allowed,specifiedFormType) {
129-
returnparamType,specifiedFormType,xerrors.Errorf("value type %q is not supported for 'form_types'",specifiedFormType)
131+
optionMsg:=""
132+
opposite:=formTypeTruthTable[paramType][!optionsExist]
133+
134+
// This extra message tells a user if they are using a valid form_type
135+
// for a 'type', but it is invalid because options do/do-not exist.
136+
// It serves as a more helpful error message.
137+
//
138+
// Eg: form_type=slider is valid for type=number, but invalid if options exist.
139+
// And this error message is more accurate than just saying "form_type=slider is
140+
// not valid for type=number".
141+
ifslices.Contains(opposite,specifiedFormType) {
142+
ifoptionsExist {
143+
optionMsg=" when options exist"
144+
}else {
145+
optionMsg=" when options do not exist"
146+
}
147+
}
148+
returnparamType,specifiedFormType,
149+
xerrors.Errorf("\"form_type\" attribute=%q is not supported for\"type\"=%q%s, choose one of %v",
150+
specifiedFormType,paramType,
151+
optionMsg,toStrings(allowed))
130152
}
131153

132154
// This is the only current special case. If 'multi-select' is selected, the type

‎provider/parameter.go

Lines changed: 132 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ func parameterDataSource() *schema.Resource {
124124
}
125125
varvaluestring
126126
ifparameter.Default!="" {
127-
err:=valueIsType(parameter.Type,parameter.Default)
127+
err:=valueIsType(parameter.Type,parameter.Default, cty.Path{cty.GetAttrStep{Name:"default"}})
128128
iferr!=nil {
129129
returnerr
130130
}
@@ -144,6 +144,8 @@ func parameterDataSource() *schema.Resource {
144144
returndiag.Errorf("ephemeral parameter requires the default property")
145145
}
146146

147+
// TODO: Should we move this into the Valid() function on
148+
// Parameter?
147149
iflen(parameter.Validation)==1 {
148150
validation:=&parameter.Validation[0]
149151
err=validation.Valid(parameter.Type,value)
@@ -153,73 +155,27 @@ func parameterDataSource() *schema.Resource {
153155
}
154156

155157
// Validate options
156-
157-
// optionType might differ from parameter.Type. This is ok, and parameter.Type
158-
// should be used for the value type, and optionType for options.
159-
varoptionTypeOptionType
160-
optionType,parameter.FormType,err=ValidateFormType(parameter.Type,len(parameter.Option),parameter.FormType)
158+
_,parameter.FormType,err=ValidateFormType(parameter.Type,len(parameter.Option),parameter.FormType)
161159
iferr!=nil {
162-
returndiag.FromErr(err)
160+
return diag.Diagnostics{
161+
{
162+
Severity:diag.Error,
163+
Summary:"Invalid form_type for parameter",
164+
Detail:err.Error(),
165+
AttributePath: cty.Path{cty.GetAttrStep{Name:"form_type"}},
166+
},
167+
}
163168
}
164169
// Set the form_type back in case the value was changed.
165170
// Eg via a default. If a user does not specify, a default value
166171
// is used and saved.
167172
rd.Set("form_type",parameter.FormType)
168173

169-
iflen(parameter.Option)>0 {
170-
names:=map[string]interface{}{}
171-
values:=map[string]interface{}{}
172-
for_,option:=rangeparameter.Option {
173-
_,exists:=names[option.Name]
174-
ifexists {
175-
returndiag.Errorf("multiple options cannot have the same name %q",option.Name)
176-
}
177-
_,exists=values[option.Value]
178-
ifexists {
179-
returndiag.Errorf("multiple options cannot have the same value %q",option.Value)
180-
}
181-
err:=valueIsType(optionType,option.Value)
182-
iferr!=nil {
183-
returnerr
184-
}
185-
values[option.Value]=nil
186-
names[option.Name]=nil
187-
}
188-
189-
ifparameter.Default!="" {
190-
ifparameter.Type==OptionTypeListString&&optionType==OptionTypeString {
191-
// If the type is list(string) and optionType is string, we have
192-
// to ensure all elements of the default exist as options.
193-
vardefaultValues []string
194-
// TODO: We do this unmarshal in a few spots. It should be standardized.
195-
err=json.Unmarshal([]byte(parameter.Default),&defaultValues)
196-
iferr!=nil {
197-
returndiag.Errorf("default value %q is not a list of strings",parameter.Default)
198-
}
199-
200-
// missing is used to construct a more helpful error message
201-
varmissing []string
202-
for_,defaultValue:=rangedefaultValues {
203-
_,defaultIsValid:=values[defaultValue]
204-
if!defaultIsValid {
205-
missing=append(missing,defaultValue)
206-
}
207-
}
208-
209-
iflen(missing)>0 {
210-
returndiag.Errorf(
211-
"default value %q is not a valid option, values %q are missing from the option",
212-
parameter.Default,strings.Join(missing,", "),
213-
)
214-
}
215-
}else {
216-
_,defaultIsValid:=values[parameter.Default]
217-
if!defaultIsValid {
218-
returndiag.Errorf("%q default value %q must be defined as one of options",parameter.FormType,parameter.Default)
219-
}
220-
}
221-
}
174+
diags:=parameter.Valid()
175+
ifdiags.HasError() {
176+
returndiags
222177
}
178+
223179
returnnil
224180
},
225181
Schema:map[string]*schema.Schema{
@@ -433,7 +389,7 @@ func fixValidationResourceData(rawConfig cty.Value, validation interface{}) (int
433389
returnvArr,nil
434390
}
435391

436-
funcvalueIsType(typOptionType,valuestring) diag.Diagnostics {
392+
funcvalueIsType(typOptionType,valuestring,attrPath cty.Path) diag.Diagnostics {
437393
switchtyp {
438394
caseOptionTypeNumber:
439395
_,err:=strconv.ParseFloat(value,64)
@@ -446,10 +402,9 @@ func valueIsType(typ OptionType, value string) diag.Diagnostics {
446402
returndiag.Errorf("%q is not a bool",value)
447403
}
448404
caseOptionTypeListString:
449-
varitems []string
450-
err:=json.Unmarshal([]byte(value),&items)
451-
iferr!=nil {
452-
returndiag.Errorf("%q is not an array of strings",value)
405+
_,diags:=valueIsListString(value,attrPath)
406+
ifdiags.HasError() {
407+
returndiags
453408
}
454409
caseOptionTypeString:
455410
// Anything is a string!
@@ -459,6 +414,102 @@ func valueIsType(typ OptionType, value string) diag.Diagnostics {
459414
returnnil
460415
}
461416

417+
func (v*Parameter)Valid() diag.Diagnostics {
418+
// optionType might differ from parameter.Type. This is ok, and parameter.Type
419+
// should be used for the value type, and optionType for options.
420+
optionType,_,err:=ValidateFormType(v.Type,len(v.Option),v.FormType)
421+
iferr!=nil {
422+
return diag.Diagnostics{
423+
{
424+
Severity:diag.Error,
425+
Summary:"Invalid form_type for parameter",
426+
Detail:err.Error(),
427+
AttributePath: cty.Path{cty.GetAttrStep{Name:"form_type"}},
428+
},
429+
}
430+
}
431+
432+
optionNames:=map[string]any{}
433+
optionValues:=map[string]any{}
434+
iflen(v.Option)>0 {
435+
for_,option:=rangev.Option {
436+
_,exists:=optionNames[option.Name]
437+
ifexists {
438+
return diag.Diagnostics{{
439+
Severity:diag.Error,
440+
Summary:"Option names must be unique.",
441+
Detail:fmt.Sprintf("multiple options found with the same name %q",option.Name),
442+
},
443+
}
444+
}
445+
_,exists=optionValues[option.Value]
446+
ifexists {
447+
return diag.Diagnostics{
448+
{
449+
Severity:diag.Error,
450+
Summary:"Option values must be unique.",
451+
Detail:fmt.Sprintf("multiple options found with the same value %q",option.Value),
452+
},
453+
}
454+
}
455+
diags:=valueIsType(optionType,option.Value, cty.Path{})
456+
ifdiags.HasError() {
457+
returndiags
458+
}
459+
optionValues[option.Value]=nil
460+
optionNames[option.Name]=nil
461+
}
462+
}
463+
464+
ifv.Default!=""&&len(v.Option)>0 {
465+
ifv.Type==OptionTypeListString&&optionType==OptionTypeString {
466+
// If the type is list(string) and optionType is string, we have
467+
// to ensure all elements of the default exist as options.
468+
defaultValues,diags:=valueIsListString(v.Default, cty.Path{cty.GetAttrStep{Name:"default"}})
469+
ifdiags.HasError() {
470+
returndiags
471+
}
472+
473+
// missing is used to construct a more helpful error message
474+
varmissing []string
475+
for_,defaultValue:=rangedefaultValues {
476+
_,defaultIsValid:=optionValues[defaultValue]
477+
if!defaultIsValid {
478+
missing=append(missing,defaultValue)
479+
}
480+
}
481+
482+
iflen(missing)>0 {
483+
return diag.Diagnostics{
484+
{
485+
Severity:diag.Error,
486+
Summary:"Default values must be a valid option",
487+
Detail:fmt.Sprintf(
488+
"default value %q is not a valid option, values %q are missing from the options",
489+
v.Default,strings.Join(missing,", "),
490+
),
491+
AttributePath: cty.Path{cty.GetAttrStep{Name:"default"}},
492+
},
493+
}
494+
}
495+
}else {
496+
_,defaultIsValid:=optionValues[v.Default]
497+
if!defaultIsValid {
498+
return diag.Diagnostics{
499+
{
500+
Severity:diag.Error,
501+
Summary:"Default value must be a valid option",
502+
Detail:fmt.Sprintf("the value %q must be defined as one of options",v.Default),
503+
AttributePath: cty.Path{cty.GetAttrStep{Name:"default"}},
504+
},
505+
}
506+
}
507+
}
508+
}
509+
510+
returnnil
511+
}
512+
462513
func (v*Validation)Valid(typOptionType,valuestring)error {
463514
iftyp!=OptionTypeNumber {
464515
if!v.MinDisabled {
@@ -519,6 +570,22 @@ func (v *Validation) Valid(typ OptionType, value string) error {
519570
returnnil
520571
}
521572

573+
funcvalueIsListString(valuestring,path cty.Path) ([]string, diag.Diagnostics) {
574+
varitems []string
575+
err:=json.Unmarshal([]byte(value),&items)
576+
iferr!=nil {
577+
returnnil, diag.Diagnostics{
578+
{
579+
Severity:diag.Error,
580+
Summary:"When using list(string) type, value must be a json encoded list of strings",
581+
Detail:fmt.Sprintf("value %q is not a valid list of strings",value),
582+
AttributePath:path,
583+
},
584+
}
585+
}
586+
returnitems,nil
587+
}
588+
522589
// ParameterEnvironmentVariable returns the environment variable to specify for
523590
// a parameter by it's name. It's hashed because spaces and special characters
524591
// can be used in parameter names that may not be valid in env vars.

‎provider/parameter_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ func TestParameter(t *testing.T) {
291291
}
292292
}
293293
`,
294-
ExpectError:regexp.MustCompile("cannot have the same name"),
294+
ExpectError:regexp.MustCompile("Option names must be unique"),
295295
}, {
296296
Name:"DuplicateOptionValue",
297297
Config:`
@@ -308,7 +308,7 @@ func TestParameter(t *testing.T) {
308308
}
309309
}
310310
`,
311-
ExpectError:regexp.MustCompile("cannot have the same value"),
311+
ExpectError:regexp.MustCompile("Option values must be unique"),
312312
}, {
313313
Name:"RequiredParameterNoDefault",
314314
Config:`

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp