@@ -21,6 +21,10 @@ import (
2121 "golang.org/x/xerrors"
2222)
2323
24+ var (
25+ defaultValuePath = cty.Path {cty.GetAttrStep {Name : "default" }}
26+ )
27+
2428type Option struct {
2529 Name string
2630 Description string
@@ -124,7 +128,7 @@ func parameterDataSource() *schema.Resource {
124128 }
125129 var value string
126130 if parameter .Default != "" {
127- err := valueIsType (parameter .Type , parameter .Default , cty. Path {cty. GetAttrStep { Name : "default" }} )
131+ err := valueIsType (parameter .Type , parameter .Default , defaultValuePath )
128132 if err != nil {
129133 return err
130134 }
@@ -144,34 +148,22 @@ func parameterDataSource() *schema.Resource {
144148 return diag .Errorf ("ephemeral parameter requires the default property" )
145149 }
146150
147- // TODO: Should we move this into the Valid() function on
148- // Parameter?
149- if len (parameter .Validation ) == 1 {
150- validation := & parameter .Validation [0 ]
151- err = validation .Valid (parameter .Type , value )
152- if err != nil {
153- return diag .FromErr (err )
154- }
151+ // Do ValidateFormType up front. If there is no error, update the
152+ // 'parameter.FormType' value to the new value. This is to handle default cases,
153+ // since the default logic is more advanced than the sdk provider schema
154+ // supports.
155+ _ , newFT , err := ValidateFormType (parameter .Type , len (parameter .Option ), parameter .FormType )
156+ if err == nil {
157+ // If there is an error, parameter.Valid will catch it.
158+ parameter .FormType = newFT
159+
160+ // Set the form_type back in case the value was changed.
161+ // Eg via a default. If a user does not specify, a default value
162+ // is used and saved.
163+ rd .Set ("form_type" , parameter .FormType )
155164 }
156165
157- // Validate options
158- _ , parameter .FormType , err = ValidateFormType (parameter .Type , len (parameter .Option ), parameter .FormType )
159- if err != nil {
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- }
168- }
169- // Set the form_type back in case the value was changed.
170- // Eg via a default. If a user does not specify, a default value
171- // is used and saved.
172- rd .Set ("form_type" , parameter .FormType )
173-
174- diags := parameter .Valid ()
166+ diags := parameter .Valid (value )
175167 if diags .HasError () {
176168 return diags
177169 }
@@ -414,10 +406,13 @@ func valueIsType(typ OptionType, value string, attrPath cty.Path) diag.Diagnosti
414406 return nil
415407}
416408
417- func (v * Parameter ) Valid () diag.Diagnostics {
409+ func (v * Parameter ) Valid (value string ) diag.Diagnostics {
410+ var err error
411+ var optionType OptionType
412+
418413 // optionType might differ from parameter.Type. This is ok, and parameter.Type
419414 // should be used for the value type, and optionType for options.
420- optionType , _ , err : = ValidateFormType (v .Type , len (v .Option ), v .FormType )
415+ optionType , v . FormType , err = ValidateFormType (v .Type , len (v .Option ), v .FormType )
421416 if err != nil {
422417 return diag.Diagnostics {
423418 {
@@ -458,55 +453,98 @@ func (v *Parameter) Valid() diag.Diagnostics {
458453 }
459454 optionValues [option .Value ] = nil
460455 optionNames [option .Name ] = nil
456+
457+ // TODO: Option values should also be validated.
458+ // v.validValue(option.Value, optionType, nil, cty.Path{})
459+ }
460+ }
461+
462+ // Validate the default value
463+ if v .Default != "" {
464+ d := v .validValue (v .Default , optionType , optionValues , defaultValuePath )
465+ if d .HasError () {
466+ return d
461467 }
462468 }
463469
464- if v .Default != "" && len (v .Option ) > 0 {
470+ // Value must always be validated
471+ d := v .validValue (value , optionType , optionValues , cty.Path {})
472+ if d .HasError () {
473+ return d
474+ }
475+
476+ return nil
477+ }
478+
479+ func (v * Parameter ) validValue (value string , optionType OptionType , optionValues map [string ]any , path cty.Path ) diag.Diagnostics {
480+ // name is used for constructing more precise error messages.
481+ name := "Value"
482+ if path .Equals (defaultValuePath ) {
483+ name = "Default value"
484+ }
485+
486+ // First validate if the value is a valid option
487+ if len (optionValues ) > 0 {
465488 if v .Type == OptionTypeListString && optionType == OptionTypeString {
466489 // If the type is list(string) and optionType is string, we have
467490 // to ensure all elements of the default exist as options.
468- defaultValues , diags := valueIsListString (v . Default , cty. Path {cty. GetAttrStep { Name : "default" }} )
491+ listValues , diags := valueIsListString (value , defaultValuePath )
469492 if diags .HasError () {
470493 return diags
471494 }
472495
473496 // missing is used to construct a more helpful error message
474497 var missing []string
475- for _ , defaultValue := range defaultValues {
476- _ , defaultIsValid := optionValues [defaultValue ]
477- if ! defaultIsValid {
478- missing = append (missing , defaultValue )
498+ for _ , listValue := range listValues {
499+ _ , isValid := optionValues [listValue ]
500+ if ! isValid {
501+ missing = append (missing , listValue )
479502 }
480503 }
481504
482505 if len (missing ) > 0 {
483506 return diag.Diagnostics {
484507 {
485508 Severity : diag .Error ,
486- Summary : "Default values must be a valid option" ,
509+ Summary : fmt . Sprintf ( "%ss must be a valid option", name ) ,
487510 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 , ", " ),
511+ "%s %q is not a valid option, values %q are missing from the options" ,
512+ name , value , strings .Join (missing , ", " ),
490513 ),
491- AttributePath : cty. Path {cty. GetAttrStep { Name : "default" }} ,
514+ AttributePath : defaultValuePath ,
492515 },
493516 }
494517 }
495518 } else {
496- _ , defaultIsValid := optionValues [v . Default ]
497- if ! defaultIsValid {
519+ _ , isValid := optionValues [value ]
520+ if ! isValid {
498521 return diag.Diagnostics {
499522 {
500523 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" }} ,
524+ Summary : fmt . Sprintf ( "%s must be a valid option", name ) ,
525+ Detail : fmt .Sprintf ("the value %q must be defined as one of options" , value ),
526+ AttributePath : path ,
504527 },
505528 }
506529 }
507530 }
508531 }
509532
533+ if len (v .Validation ) == 1 {
534+ validCheck := & v .Validation [0 ]
535+ err := validCheck .Valid (v .Type , value )
536+ if err != nil {
537+ return diag.Diagnostics {
538+ {
539+ Severity : diag .Error ,
540+ Summary : fmt .Sprintf ("Invalid parameter %s according to 'validation' block" , strings .ToLower (name )),
541+ Detail : err .Error (),
542+ AttributePath : path ,
543+ },
544+ }
545+ }
546+ }
547+
510548 return nil
511549}
512550
0 commit comments