diff --git a/pkg/k2/constructs/dynamic_value.go b/pkg/k2/constructs/dynamic_value.go index eb8732ce..132bf6b2 100644 --- a/pkg/k2/constructs/dynamic_value.go +++ b/pkg/k2/constructs/dynamic_value.go @@ -12,7 +12,7 @@ import ( "github.com/klothoplatform/klotho/pkg/async" "github.com/klothoplatform/klotho/pkg/construct" template2 "github.com/klothoplatform/klotho/pkg/k2/constructs/template" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" + "github.com/klothoplatform/klotho/pkg/k2/constructs/template/properties" "github.com/klothoplatform/klotho/pkg/k2/model" "github.com/klothoplatform/klotho/pkg/templateutils" "go.uber.org/zap" @@ -52,7 +52,7 @@ func (ctx DynamicValueContext) ExecuteUnmarshal(tmpl string, data any, value any } func (ctx DynamicValueContext) Unmarshal(data *bytes.Buffer, v any) error { - return execution.UnmarshalAny(data, v) + return properties.UnmarshalAny(data, v) } // ExecuteTemplateUnmarshal executes the template tmpl using data as input and unmarshals the value into v diff --git a/pkg/k2/constructs/template/execution/unmarshal.go b/pkg/k2/constructs/template/execution/unmarshal.go deleted file mode 100644 index 2c0dbcbb..00000000 --- a/pkg/k2/constructs/template/execution/unmarshal.go +++ /dev/null @@ -1,18 +0,0 @@ -package execution - -import ( - "fmt" - "github.com/klothoplatform/klotho/pkg/k2/model" -) - -func ExecuteUnmarshalAsURN(ctx Context, tmpl string, data any) (model.URN, error) { - var selector model.URN - err := ctx.ExecuteUnmarshal(tmpl, data, &selector) - if err != nil { - return selector, err - } - if selector.IsZero() { - return selector, fmt.Errorf("selector '%s' is zero", tmpl) - } - return selector, nil -} diff --git a/pkg/k2/constructs/template/inputs/properties_template.go b/pkg/k2/constructs/template/inputs/properties_template.go index 135e0b78..9756ca0f 100644 --- a/pkg/k2/constructs/template/inputs/properties_template.go +++ b/pkg/k2/constructs/template/inputs/properties_template.go @@ -6,7 +6,6 @@ import ( "regexp" "strings" - "github.com/google/uuid" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/properties" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "gopkg.in/yaml.v3" @@ -22,37 +21,45 @@ type ( Name string `json:"name" yaml:"name"` // Type defines the type of the property Type string `json:"type" yaml:"type"` - + // Description defines the description of the property Description string `json:"description" yaml:"description"` - + // DefaultValue defines the default value of the property DefaultValue any `json:"default_value" yaml:"default_value"` - + // Required defines whether the property is required Required bool `json:"required" yaml:"required"` - //OperationalRule *knowledgebase.PropertyRule `json:"operational_rule" yaml:"operational_rule"` - + // Properties defines the sub properties of a key_value_list, map, list, or set Properties InputTemplateMap `json:"properties" yaml:"properties"` // MinLength defines the minimum length of a string, list, set, or map (number of entries) MinLength *int `yaml:"min_length"` + // MaxLength defines the maximum length of a string, list, set, or map (number of entries) MaxLength *int `yaml:"max_length"` + // MinValue defines the minimum value of an int or float MinValue *float64 `yaml:"min_value"` + // MaxValue defines the maximum value of an int or float MaxValue *float64 `yaml:"max_value"` // UniqueItems defines whether the items in a list or set must be unique UniqueItems *bool `yaml:"unique_items"` // UniqueKeys defines whether the keys in a map must be unique (default true) UniqueKeys *bool `yaml:"unique_keys"` - - SanitizeTmpl string `yaml:"sanitize"` + // SanitizeTmpl is a go template to sanitize user input when setting the property + SanitizeTmpl string `yaml:"sanitize"` + // AllowedValues defines an enumeration of allowed values for a string, int, float, or bool AllowedValues []string `yaml:"allowed_values"` - KeyProperty *InputTemplate `yaml:"key_property"` + // KeyProperty is the property of the keys in a key_value_list or map + KeyProperty *InputTemplate `yaml:"key_property"` + // ValueProperty is the property of the values in a key_value_list or map ValueProperty *InputTemplate `yaml:"value_property"` + // ItemProperty is the property of the items in a list or set ItemProperty *InputTemplate `yaml:"item_property"` + // Path is the path to the property in the template + // this field is derived and is not part of the yaml Path string `json:"-" yaml:"-"` } @@ -302,9 +309,7 @@ var fieldConversion = map[string]FieldConverterFunc{ if sanitizeTmpl == "" { return nil } - // generate random uuid as the name of the template - name := uuid.New().String() - tmpl, err := property.NewSanitizationTmpl(name, sanitizeTmpl) + tmpl, err := property.NewSanitizationTmpl(kp.Details().Name, sanitizeTmpl) if err != nil { return err } diff --git a/pkg/k2/constructs/template/inputs/properties_template_test.go b/pkg/k2/constructs/template/inputs/properties_template_test.go index 4b958b3c..7cb57b66 100644 --- a/pkg/k2/constructs/template/inputs/properties_template_test.go +++ b/pkg/k2/constructs/template/inputs/properties_template_test.go @@ -6,6 +6,7 @@ import ( "github.com/klothoplatform/klotho/pkg/k2/constructs/template/properties" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_ConvertProperty(t *testing.T) { @@ -15,26 +16,236 @@ func Test_ConvertProperty(t *testing.T) { expected property.Property }{ { - name: "Get string property type", + name: "Convert string property type", property: InputTemplate{ Type: "string", Name: "test", Path: "test", Required: true, AllowedValues: []string{"test1", "test2"}, - SanitizeTmpl: "test", + DefaultValue: "test", + }, + expected: &properties.StringProperty{ + SharedPropertyFields: properties.SharedPropertyFields{ + DefaultValue: "test", + }, + PropertyDetails: property.PropertyDetails{ + Name: "test", + Required: true, + Path: "test", + }, + AllowedValues: []string{"test1", "test2"}, + }, + }, + { + name: "Convert int property type", + property: InputTemplate{ + Type: "int", + Name: "test", + Path: "test", + Required: true, + }, + expected: &properties.IntProperty{ + PropertyDetails: property.PropertyDetails{ + Name: "test", + Required: true, + Path: "test", + }, + }, + }, + { + name: "Convert float property type", + property: InputTemplate{ + Type: "float", + Name: "test", + Path: "test", + Required: true, + }, + expected: &properties.FloatProperty{ + PropertyDetails: property.PropertyDetails{ + Name: "test", + Required: true, + Path: "test", + }, + }, + }, + { + name: "Convert bool property type", + property: InputTemplate{ + Type: "bool", + Name: "test", + Path: "test", + Required: true, + }, + expected: &properties.BoolProperty{ + PropertyDetails: property.PropertyDetails{ + Name: "test", + Required: true, + Path: "test", + }, + }, + }, + { + name: "Convert map property type", + property: InputTemplate{ + Type: "map(string,string)", + Name: "test", + Path: "test", + Required: true, + KeyProperty: &InputTemplate{ + Type: "string", + }, + ValueProperty: &InputTemplate{ + Type: "string", + }, + }, + expected: &properties.MapProperty{ + PropertyDetails: property.PropertyDetails{ + Name: "test", + Required: true, + Path: "test", + }, + Properties: map[string]property.Property{}, + KeyProperty: &properties.StringProperty{}, + ValueProperty: &properties.StringProperty{}, + }, + }, + { + name: "Convert list property type", + property: InputTemplate{ + Type: "list(string)", + Name: "test", + Path: "test", + Required: true, + }, + expected: &properties.ListProperty{ + PropertyDetails: property.PropertyDetails{ + Name: "test", + Required: true, + Path: "test", + }, + ItemProperty: &properties.StringProperty{}, + Properties: map[string]property.Property{}, + }, + }, + { + name: "Convert set property type", + property: InputTemplate{ + Type: "set(string)", + Name: "test", + Path: "test", + Required: true, + ItemProperty: &InputTemplate{ + Type: "string", + }, + }, + expected: &properties.SetProperty{ + PropertyDetails: property.PropertyDetails{ + Name: "test", + Required: true, + Path: "test", + }, + ItemProperty: &properties.StringProperty{}, + Properties: map[string]property.Property{}, + }, + }, + { + name: "Convert key_value_list property type", + property: InputTemplate{ + Type: "key_value_list(string,string)", + Name: "test", + Path: "test", + Required: true, + }, + expected: &properties.KeyValueListProperty{ + PropertyDetails: property.PropertyDetails{ + Name: "test", + Required: true, + Path: "test", + }, + KeyProperty: &properties.StringProperty{PropertyDetails: property.PropertyDetails{Name: "Key"}}, + ValueProperty: &properties.StringProperty{PropertyDetails: property.PropertyDetails{Name: "Value"}}, + }, + }, + { + name: "Convert key_value_list property type with custom key and value properties", + property: InputTemplate{ + Type: "key_value_list(string,string)", + Name: "test", + Path: "test", + KeyProperty: &InputTemplate{ + Type: "string", + Name: "CustomKeyKey", + }, + ValueProperty: &InputTemplate{ + Type: "string", + Name: "CustomValueKey", + }, + }, + expected: &properties.KeyValueListProperty{ + PropertyDetails: property.PropertyDetails{ + Name: "test", + Path: "test", + }, + KeyProperty: &properties.StringProperty{PropertyDetails: property.PropertyDetails{Name: "CustomKeyKey"}}, + ValueProperty: &properties.StringProperty{PropertyDetails: property.PropertyDetails{Name: "CustomValueKey"}}, + }, + }, + { + name: "Convert construct property type", + property: InputTemplate{ + Type: "construct", + Name: "test", + Path: "test", + Required: true, + }, + expected: &properties.ConstructProperty{ + PropertyDetails: property.PropertyDetails{ + Name: "test", + Required: true, + Path: "test", + }, + }, + }, + { + name: "Convert any property type", + property: InputTemplate{ + Type: "any", + Name: "test", + Path: "test", + Required: true, + }, + expected: &properties.AnyProperty{ + PropertyDetails: property.PropertyDetails{ + Name: "test", + Required: true, + Path: "test", + }, + }, + }, + { + name: "Convert path property type", + property: InputTemplate{ + Type: "path", + Name: "test", + Path: "test", + Required: true, + }, + expected: &properties.PathProperty{ + PropertyDetails: property.PropertyDetails{ + Name: "test", + Required: true, + Path: "test", + }, }, - expected: &properties.StringProperty{}, }, } + for _, test := range tests { t.Run(test.name, func(t *testing.T) { - assert := assert.New(t) actual, err := test.property.Convert() - if assert.NoError(err, "Expected no error, but got: %v", err) { - return - } - assert.Equal(actual, test.expected, "expected %v, got %v", test.expected, actual) + require.NoError(t, err) + assert.EqualValuesf(t, actual, test.expected, "expected %v, got %v", test.expected, actual) }) } } diff --git a/pkg/k2/constructs/template/properties/any_property.go b/pkg/k2/constructs/template/properties/any_property.go index e7c01f6b..96af162b 100644 --- a/pkg/k2/constructs/template/properties/any_property.go +++ b/pkg/k2/constructs/template/properties/any_property.go @@ -2,8 +2,8 @@ package properties import ( "fmt" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" ) @@ -42,14 +42,14 @@ func (a *AnyProperty) Clone() property.Property { return &clone } -func (a *AnyProperty) GetDefaultValue(ctx execution.Context, data any) (any, error) { +func (a *AnyProperty) GetDefaultValue(ctx property.ExecutionContext, data any) (any, error) { if a.DefaultValue == nil { return nil, nil } return a.Parse(a.DefaultValue, ctx, data) } -func (a *AnyProperty) Parse(value any, ctx execution.Context, data any) (any, error) { +func (a *AnyProperty) Parse(value any, ctx property.ExecutionContext, data any) (any, error) { if val, ok := value.(string); ok { // check if its any other template string var result any diff --git a/pkg/k2/constructs/template/properties/any_property_test.go b/pkg/k2/constructs/template/properties/any_property_test.go index 5e0836fa..d7263de2 100644 --- a/pkg/k2/constructs/template/properties/any_property_test.go +++ b/pkg/k2/constructs/template/properties/any_property_test.go @@ -1,11 +1,11 @@ package properties import ( + "testing" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "github.com/stretchr/testify/assert" - "testing" ) // Testing the SetProperty method for different cases @@ -218,7 +218,7 @@ func Test_AnyProperty_Parse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := tt.property.Parse(tt.input, execution.DefaultExecutionContext{}, nil) + result, err := tt.property.Parse(tt.input, DefaultExecutionContext{}, nil) if tt.wantError { assert.Error(t, err) } else { diff --git a/pkg/k2/constructs/template/properties/bool_property.go b/pkg/k2/constructs/template/properties/bool_property.go index 5c352ddb..f62078c5 100644 --- a/pkg/k2/constructs/template/properties/bool_property.go +++ b/pkg/k2/constructs/template/properties/bool_property.go @@ -3,8 +3,8 @@ package properties import ( "errors" "fmt" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" ) @@ -51,14 +51,14 @@ func (b *BoolProperty) Details() *property.PropertyDetails { return &b.PropertyDetails } -func (b *BoolProperty) GetDefaultValue(ctx execution.Context, data any) (any, error) { +func (b *BoolProperty) GetDefaultValue(ctx property.ExecutionContext, data any) (any, error) { if b.DefaultValue == nil { return nil, nil } return b.Parse(b.DefaultValue, ctx, data) } -func (b *BoolProperty) Parse(value any, ctx execution.Context, data any) (any, error) { +func (b *BoolProperty) Parse(value any, ctx property.ExecutionContext, data any) (any, error) { if val, ok := value.(string); ok { var result bool err := ctx.ExecuteUnmarshal(val, data, &result) diff --git a/pkg/k2/constructs/template/properties/construct_property.go b/pkg/k2/constructs/template/properties/construct_property.go index 63a8c297..15f90467 100644 --- a/pkg/k2/constructs/template/properties/construct_property.go +++ b/pkg/k2/constructs/template/properties/construct_property.go @@ -3,8 +3,8 @@ package properties import ( "errors" "fmt" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "github.com/klothoplatform/klotho/pkg/k2/model" ) @@ -78,16 +78,16 @@ func (r *ConstructProperty) Clone() property.Property { return &clone } -func (r *ConstructProperty) GetDefaultValue(ctx execution.Context, data any) (any, error) { +func (r *ConstructProperty) GetDefaultValue(ctx property.ExecutionContext, data any) (any, error) { if r.DefaultValue == nil { return nil, nil } return r.Parse(r.DefaultValue, ctx, data) } -func (r *ConstructProperty) Parse(value any, ctx execution.Context, data any) (any, error) { +func (r *ConstructProperty) Parse(value any, ctx property.ExecutionContext, data any) (any, error) { if val, ok := value.(string); ok { - urn, err := execution.ExecuteUnmarshalAsURN(ctx, val, data) + urn, err := ExecuteUnmarshalAsURN(ctx, val, data) if err != nil { return nil, fmt.Errorf("invalid construct URN %v", val) } diff --git a/pkg/k2/constructs/template/properties/float_property.go b/pkg/k2/constructs/template/properties/float_property.go index 87878ca7..8cd74973 100644 --- a/pkg/k2/constructs/template/properties/float_property.go +++ b/pkg/k2/constructs/template/properties/float_property.go @@ -3,8 +3,8 @@ package properties import ( "errors" "fmt" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" ) @@ -60,14 +60,14 @@ func (f *FloatProperty) Clone() property.Property { return &clone } -func (f *FloatProperty) GetDefaultValue(ctx execution.Context, data any) (any, error) { +func (f *FloatProperty) GetDefaultValue(ctx property.ExecutionContext, data any) (any, error) { if f.DefaultValue == nil { return nil, nil } return f.Parse(f.DefaultValue, ctx, data) } -func (f *FloatProperty) Parse(value any, ctx execution.Context, data any) (any, error) { +func (f *FloatProperty) Parse(value any, ctx property.ExecutionContext, data any) (any, error) { if val, ok := value.(string); ok { var result float32 err := ctx.ExecuteUnmarshal(val, data, &result) diff --git a/pkg/k2/constructs/template/properties/float_property_test.go b/pkg/k2/constructs/template/properties/float_property_test.go index 89e8fc38..0e6b14a6 100644 --- a/pkg/k2/constructs/template/properties/float_property_test.go +++ b/pkg/k2/constructs/template/properties/float_property_test.go @@ -1,11 +1,11 @@ package properties import ( + "testing" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "github.com/stretchr/testify/assert" - "testing" ) // Testing the SetProperty method for different cases @@ -218,7 +218,7 @@ func Test_FloatProperty_Parse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := tt.property.Parse(tt.input, execution.DefaultExecutionContext{}, nil) + result, err := tt.property.Parse(tt.input, DefaultExecutionContext{}, nil) if tt.wantError { assert.Error(t, err) } else { diff --git a/pkg/k2/constructs/template/properties/int_property.go b/pkg/k2/constructs/template/properties/int_property.go index a3c46d2b..64c901b8 100644 --- a/pkg/k2/constructs/template/properties/int_property.go +++ b/pkg/k2/constructs/template/properties/int_property.go @@ -3,10 +3,10 @@ package properties import ( "errors" "fmt" + "math" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" - "math" ) type ( @@ -54,14 +54,14 @@ func (i *IntProperty) Clone() property.Property { return &clone } -func (i *IntProperty) GetDefaultValue(ctx execution.Context, data any) (any, error) { +func (i *IntProperty) GetDefaultValue(ctx property.ExecutionContext, data any) (any, error) { if i.DefaultValue == nil { return nil, nil } return i.Parse(i.DefaultValue, ctx, data) } -func (i *IntProperty) Parse(value any, ctx execution.Context, data any) (any, error) { +func (i *IntProperty) Parse(value any, ctx property.ExecutionContext, data any) (any, error) { if val, ok := value.(string); ok { var result int diff --git a/pkg/k2/constructs/template/properties/int_property_test.go b/pkg/k2/constructs/template/properties/int_property_test.go index b039fa98..b1e7513e 100644 --- a/pkg/k2/constructs/template/properties/int_property_test.go +++ b/pkg/k2/constructs/template/properties/int_property_test.go @@ -1,11 +1,11 @@ package properties import ( + "testing" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "github.com/stretchr/testify/assert" - "testing" ) // Testing the SetProperty method for different cases @@ -210,7 +210,7 @@ func Test_IntProperty_Parse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := tt.property.Parse(tt.input, execution.DefaultExecutionContext{}, nil) + result, err := tt.property.Parse(tt.input, DefaultExecutionContext{}, nil) if tt.wantError { assert.Error(t, err) } else { diff --git a/pkg/k2/constructs/template/properties/key_value_list.go b/pkg/k2/constructs/template/properties/key_value_list.go index 28dc5745..696d4919 100644 --- a/pkg/k2/constructs/template/properties/key_value_list.go +++ b/pkg/k2/constructs/template/properties/key_value_list.go @@ -3,10 +3,10 @@ package properties import ( "errors" "fmt" + "reflect" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" - "reflect" ) type ( @@ -95,14 +95,14 @@ func (kvl *KeyValueListProperty) Clone() property.Property { return &clone } -func (kvl *KeyValueListProperty) GetDefaultValue(ctx execution.Context, data any) (any, error) { +func (kvl *KeyValueListProperty) GetDefaultValue(ctx property.ExecutionContext, data any) (any, error) { if kvl.DefaultValue == nil { return nil, nil } return kvl.Parse(kvl.DefaultValue, ctx, data) } -func (kvl *KeyValueListProperty) Parse(value any, ctx execution.Context, data any) (any, error) { +func (kvl *KeyValueListProperty) Parse(value any, ctx property.ExecutionContext, data any) (any, error) { list, err := kvl.mapToList(value) if err != nil { return nil, err diff --git a/pkg/k2/constructs/template/properties/key_value_list_test.go b/pkg/k2/constructs/template/properties/key_value_list_test.go index 3a1d7a72..d85b0b05 100644 --- a/pkg/k2/constructs/template/properties/key_value_list_test.go +++ b/pkg/k2/constructs/template/properties/key_value_list_test.go @@ -1,11 +1,11 @@ package properties import ( + "testing" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "github.com/stretchr/testify/assert" - "testing" ) func Test_KeyValueListProperty_SetProperty(t *testing.T) { @@ -189,7 +189,7 @@ func Test_KeyValueListProperty_GetDefaultValue(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx := execution.DefaultExecutionContext{} + ctx := DefaultExecutionContext{} result, err := tt.property.GetDefaultValue(ctx, nil) if tt.wantError { assert.Error(t, err) @@ -230,7 +230,7 @@ func Test_KeyValueListProperty_Parse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx := execution.DefaultExecutionContext{} + ctx := DefaultExecutionContext{} result, err := tt.property.Parse(tt.input, ctx, nil) if tt.wantError { assert.Error(t, err) diff --git a/pkg/k2/constructs/template/properties/list_property.go b/pkg/k2/constructs/template/properties/list_property.go index 9e759207..0d40e442 100644 --- a/pkg/k2/constructs/template/properties/list_property.go +++ b/pkg/k2/constructs/template/properties/list_property.go @@ -3,12 +3,12 @@ package properties import ( "errors" "fmt" - "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "reflect" "strings" + "github.com/klothoplatform/klotho/pkg/construct" + "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" + "github.com/klothoplatform/klotho/pkg/collectionutil" ) @@ -101,14 +101,14 @@ func (l *ListProperty) Clone() property.Property { return &clone } -func (list *ListProperty) GetDefaultValue(ctx execution.Context, data any) (any, error) { +func (list *ListProperty) GetDefaultValue(ctx property.ExecutionContext, data any) (any, error) { if list.DefaultValue == nil { return nil, nil } return list.Parse(list.DefaultValue, ctx, data) } -func (list *ListProperty) Parse(value any, ctx execution.Context, data any) (any, error) { +func (list *ListProperty) Parse(value any, ctx property.ExecutionContext, data any) (any, error) { var result []any val, ok := value.([]any) diff --git a/pkg/k2/constructs/template/properties/list_property_test.go b/pkg/k2/constructs/template/properties/list_property_test.go index 1c5d855d..e6cfe470 100644 --- a/pkg/k2/constructs/template/properties/list_property_test.go +++ b/pkg/k2/constructs/template/properties/list_property_test.go @@ -1,11 +1,11 @@ package properties import ( + "testing" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "github.com/stretchr/testify/assert" - "testing" ) // Testing the SetProperty method for different cases @@ -215,7 +215,7 @@ func Test_ListProperty_Parse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := tt.property.Parse(tt.input, execution.DefaultExecutionContext{}, nil) + result, err := tt.property.Parse(tt.input, DefaultExecutionContext{}, nil) if tt.wantError { assert.Error(t, err) return diff --git a/pkg/k2/constructs/template/properties/map_property.go b/pkg/k2/constructs/template/properties/map_property.go index 7cf9504e..7bf101b1 100644 --- a/pkg/k2/constructs/template/properties/map_property.go +++ b/pkg/k2/constructs/template/properties/map_property.go @@ -3,10 +3,10 @@ package properties import ( "errors" "fmt" + "reflect" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" - "reflect" ) type ( @@ -82,14 +82,14 @@ func (m *MapProperty) Clone() property.Property { return &clone } -func (m *MapProperty) GetDefaultValue(ctx execution.Context, data any) (any, error) { +func (m *MapProperty) GetDefaultValue(ctx property.ExecutionContext, data any) (any, error) { if m.DefaultValue == nil { return nil, nil } return m.Parse(m.DefaultValue, ctx, data) } -func (m *MapProperty) Parse(value any, ctx execution.Context, data any) (any, error) { +func (m *MapProperty) Parse(value any, ctx property.ExecutionContext, data any) (any, error) { result := map[string]any{} mapVal, ok := value.(map[string]any) diff --git a/pkg/k2/constructs/template/properties/map_propery_test.go b/pkg/k2/constructs/template/properties/map_propery_test.go index 5e1548c5..2f3a81c3 100644 --- a/pkg/k2/constructs/template/properties/map_propery_test.go +++ b/pkg/k2/constructs/template/properties/map_propery_test.go @@ -1,11 +1,11 @@ package properties import ( + "testing" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "github.com/stretchr/testify/assert" - "testing" ) func Test_MapProperty_SetProperty(t *testing.T) { @@ -222,7 +222,7 @@ func Test_MapProperty_GetDefaultValue(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx := execution.DefaultExecutionContext{} + ctx := DefaultExecutionContext{} result, err := tt.property.GetDefaultValue(ctx, nil) if tt.wantError { assert.Error(t, err) @@ -269,7 +269,7 @@ func Test_MapProperty_Parse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx := execution.DefaultExecutionContext{} + ctx := DefaultExecutionContext{} result, err := tt.property.Parse(tt.input, ctx, nil) if tt.wantError { assert.Error(t, err) diff --git a/pkg/k2/constructs/template/properties/path_property.go b/pkg/k2/constructs/template/properties/path_property.go index d8ac7fd8..4023437b 100644 --- a/pkg/k2/constructs/template/properties/path_property.go +++ b/pkg/k2/constructs/template/properties/path_property.go @@ -3,13 +3,13 @@ package properties import ( "errors" "fmt" - "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "os" "path/filepath" "strings" + "github.com/klothoplatform/klotho/pkg/construct" + "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" + "github.com/klothoplatform/klotho/pkg/collectionutil" ) @@ -66,14 +66,14 @@ func (p *PathProperty) Clone() property.Property { return &clone } -func (p *PathProperty) GetDefaultValue(ctx execution.Context, data any) (any, error) { +func (p *PathProperty) GetDefaultValue(ctx property.ExecutionContext, data any) (any, error) { if p.DefaultValue == nil { return p.ZeroValue(), nil } return p.Parse(p.DefaultValue, ctx, data) } -func (p *PathProperty) Parse(value any, ctx execution.Context, data any) (any, error) { +func (p *PathProperty) Parse(value any, ctx property.ExecutionContext, data any) (any, error) { strVal := "" switch val := value.(type) { case string: diff --git a/pkg/k2/constructs/template/execution/context.go b/pkg/k2/constructs/template/properties/properties.go similarity index 86% rename from pkg/k2/constructs/template/execution/context.go rename to pkg/k2/constructs/template/properties/properties.go index 583ee58e..68124a2e 100644 --- a/pkg/k2/constructs/template/execution/context.go +++ b/pkg/k2/constructs/template/properties/properties.go @@ -1,26 +1,21 @@ -package execution +package properties import ( "bytes" "encoding" "encoding/json" "fmt" - "github.com/klothoplatform/klotho/pkg/templateutils" "reflect" "strconv" "strings" "text/template" + + "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" + "github.com/klothoplatform/klotho/pkg/k2/model" + "github.com/klothoplatform/klotho/pkg/templateutils" ) type ( - // Context is an interface that defines the methods to execute a template and decode the result into a value - Context interface { - // ExecuteUnmarshal executes the template tmpl using data as input and unmarshals the value into v - ExecuteUnmarshal(tmpl string, data any, v any) error - // Unmarshal unmarshals the template result into a value - Unmarshal(data *bytes.Buffer, v any) error - } - DefaultExecutionContext struct{} ) @@ -129,3 +124,15 @@ func UnmarshalAny(data *bytes.Buffer, v any) error { return err } + +func ExecuteUnmarshalAsURN(ctx property.ExecutionContext, tmpl string, data any) (model.URN, error) { + var selector model.URN + err := ctx.ExecuteUnmarshal(tmpl, data, &selector) + if err != nil { + return selector, err + } + if selector.IsZero() { + return selector, fmt.Errorf("selector '%s' is zero", tmpl) + } + return selector, nil +} diff --git a/pkg/k2/constructs/template/properties/set_property.go b/pkg/k2/constructs/template/properties/set_property.go index fe47fba7..e2fc8df0 100644 --- a/pkg/k2/constructs/template/properties/set_property.go +++ b/pkg/k2/constructs/template/properties/set_property.go @@ -3,10 +3,10 @@ package properties import ( "errors" "fmt" + "reflect" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" - "reflect" "github.com/klothoplatform/klotho/pkg/set" ) @@ -91,14 +91,14 @@ func (s *SetProperty) Clone() property.Property { return &clone } -func (s *SetProperty) GetDefaultValue(ctx execution.Context, data any) (any, error) { +func (s *SetProperty) GetDefaultValue(ctx property.ExecutionContext, data any) (any, error) { if s.DefaultValue == nil { return nil, nil } return s.Parse(s.DefaultValue, ctx, data) } -func (s *SetProperty) Parse(value any, ctx execution.Context, data any) (any, error) { +func (s *SetProperty) Parse(value any, ctx property.ExecutionContext, data any) (any, error) { var result = set.HashedSet[string, any]{ Hasher: func(s any) string { return fmt.Sprintf("%v", s) diff --git a/pkg/k2/constructs/template/properties/set_property_test.go b/pkg/k2/constructs/template/properties/set_property_test.go index 44ae9274..e9fc697b 100644 --- a/pkg/k2/constructs/template/properties/set_property_test.go +++ b/pkg/k2/constructs/template/properties/set_property_test.go @@ -2,13 +2,13 @@ package properties import ( "fmt" + "testing" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" "github.com/klothoplatform/klotho/pkg/knowledgebase" "github.com/klothoplatform/klotho/pkg/set" "github.com/stretchr/testify/assert" - "testing" ) func Test_SetProperty_SetProperty(t *testing.T) { @@ -255,7 +255,7 @@ func Test_SetParse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert := assert.New(t) - ctx := execution.DefaultExecutionContext{} + ctx := DefaultExecutionContext{} actual, err := tt.property.Parse(tt.value, ctx, nil) if tt.wantErr { assert.Error(err) diff --git a/pkg/k2/constructs/template/properties/string_property.go b/pkg/k2/constructs/template/properties/string_property.go index 3cf7ac61..218dcc8f 100644 --- a/pkg/k2/constructs/template/properties/string_property.go +++ b/pkg/k2/constructs/template/properties/string_property.go @@ -3,10 +3,10 @@ package properties import ( "errors" "fmt" + "strings" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" "github.com/klothoplatform/klotho/pkg/k2/constructs/template/property" - "strings" "github.com/klothoplatform/klotho/pkg/collectionutil" ) @@ -56,14 +56,14 @@ func (str *StringProperty) Clone() property.Property { return &clone } -func (str *StringProperty) GetDefaultValue(ctx execution.Context, data any) (any, error) { +func (str *StringProperty) GetDefaultValue(ctx property.ExecutionContext, data any) (any, error) { if str.DefaultValue == nil { return nil, nil } return str.Parse(str.DefaultValue, ctx, data) } -func (str *StringProperty) Parse(value any, ctx execution.Context, data any) (any, error) { +func (str *StringProperty) Parse(value any, ctx property.ExecutionContext, data any) (any, error) { switch val := value.(type) { case string: err := ctx.ExecuteUnmarshal(val, data, &val) diff --git a/pkg/k2/constructs/template/property/construct_type.go b/pkg/k2/constructs/template/property/construct_type.go index 9b200cb1..f066e7d3 100644 --- a/pkg/k2/constructs/template/property/construct_type.go +++ b/pkg/k2/constructs/template/property/construct_type.go @@ -39,29 +39,10 @@ func (c *ConstructType) UnmarshalYAML(value *yaml.Node) error { return nil } -func ParseConstructType(id string) (ConstructType, error) { - // Parse a construct type from a string - parts := strings.Split(id, ".") - if len(parts) < 2 { - return ConstructType{}, fmt.Errorf("invalid construct type: %s", id) - } - return ConstructType{ - Package: strings.Join(parts[:len(parts)-1], "."), - Name: parts[len(parts)-1], - }, nil -} - func (c *ConstructType) String() string { return fmt.Sprintf("%s.%s", c.Package, c.Name) } -func (c *ConstructType) FromURN(urn model.URN) error { - if urn.Type != "construct" { - return fmt.Errorf("invalid urn type: %s", urn.Type) - } - return c.FromString(urn.Subtype) -} - func (c *ConstructType) FromString(id string) error { parts := strings.Split(id, ".") if len(parts) < 2 { @@ -71,3 +52,16 @@ func (c *ConstructType) FromString(id string) error { c.Name = parts[len(parts)-1] return nil } + +func ParseConstructType(id string) (ConstructType, error) { + var c ConstructType + err := c.FromString(id) + return c, err +} + +func (c *ConstructType) FromURN(urn model.URN) error { + if urn.Type != "construct" { + return fmt.Errorf("invalid urn type: %s", urn.Type) + } + return c.FromString(urn.Subtype) +} diff --git a/pkg/k2/constructs/template/property/interfaces.go b/pkg/k2/constructs/template/property/interfaces.go index 0e1188d4..9d7a3e31 100644 --- a/pkg/k2/constructs/template/property/interfaces.go +++ b/pkg/k2/constructs/template/property/interfaces.go @@ -1,60 +1,71 @@ package property import ( + "bytes" + "github.com/klothoplatform/klotho/pkg/construct" - "github.com/klothoplatform/klotho/pkg/k2/constructs/template/execution" ) -type Property interface { - // SetProperty sets the value of the property on the properties - SetProperty(properties construct.Properties, value any) error - // AppendProperty appends the value to the property on the properties - AppendProperty(properties construct.Properties, value any) error - // RemoveProperty removes the value from the property on the properties - RemoveProperty(properties construct.Properties, value any) error - // Details returns the property details for the property - Details() *PropertyDetails - // Clone returns a clone of the property - Clone() Property - - // Type returns the string representation of the property's type, as it should appear in a template - Type() string - // GetDefaultValue returns the default value for the property, - // pertaining to the specific data being passed in for execution - GetDefaultValue(ctx execution.Context, data any) (any, error) - // Validate ensures the value is valid for the property to `Set` (not `Append` for collection types) - // and returns an error if it is not - Validate(properties construct.Properties, value any) error - // SubProperties returns the sub properties of the input, if any. - // This is used for inputs that are complex structures, such as lists, sets, or maps - SubProperties() PropertyMap - // Parse parses a given value to ensure it is the correct type for the property. - // If the given value cannot be converted to the respective property type an error is returned. - // The returned value will always be the correct type for the property - Parse(value any, ctx execution.Context, data any) (any, error) - // ZeroValue returns the zero value for the property type - ZeroValue() any - // Contains returns true if the value contains the given value - Contains(value any, contains any) bool -} - -type MapProperty interface { - // Key returns the property representing the keys of the map - Key() Property - // Value returns the property representing the values of the map - Value() Property -} - -type CollectionProperty interface { - // Item returns the structure of the items within the collection - Item() Property -} - -type Properties interface { - Clone() Properties - ForEach(c construct.Properties, f func(p Property) error) error - Get(key string) (Property, bool) - Set(key string, value Property) - Remove(key string) - AsMap() map[string]Property -} +type ( + // ExecutionContext defines the methods to execute a go template and decode the result into a value + ExecutionContext interface { + // ExecuteUnmarshal executes the template tmpl using data as input and unmarshals the value into v + ExecuteUnmarshal(tmpl string, data any, v any) error + // Unmarshal unmarshals the template result into a value + Unmarshal(data *bytes.Buffer, v any) error + } + + Property interface { + // SetProperty sets the value of the property on the properties + SetProperty(properties construct.Properties, value any) error + // AppendProperty appends the value to the property on the properties + AppendProperty(properties construct.Properties, value any) error + // RemoveProperty removes the value from the property on the properties + RemoveProperty(properties construct.Properties, value any) error + // Details returns the property details for the property + Details() *PropertyDetails + // Clone returns a clone of the property + Clone() Property + + // Type returns the string representation of the property's type, as it should appear in a template + Type() string + // GetDefaultValue returns the default value for the property, + // pertaining to the specific data being passed in for execution + GetDefaultValue(ctx ExecutionContext, data any) (any, error) + // Validate ensures the value is valid for the property to `Set` (not `Append` for collection types) + // and returns an error if it is not + Validate(properties construct.Properties, value any) error + // SubProperties returns the sub properties of the input, if any. + // This is used for inputs that are complex structures, such as lists, sets, or maps + SubProperties() PropertyMap + // Parse parses a given value to ensure it is the correct type for the property. + // If the given value cannot be converted to the respective property type an error is returned. + // The returned value will always be the correct type for the property + Parse(value any, ctx ExecutionContext, data any) (any, error) + // ZeroValue returns the zero value for the property type + ZeroValue() any + // Contains returns true if the value contains the given value + Contains(value any, contains any) bool + } + + MapProperty interface { + // Key returns the property representing the keys of the map + Key() Property + // Value returns the property representing the values of the map + Value() Property + } + + CollectionProperty interface { + // Item returns the structure of the items within the collection + Item() Property + } + + Properties interface { + Clone() Properties + ForEach(c construct.Properties, f func(p Property) error) error + Get(key string) (Property, bool) + Set(key string, value Property) + Remove(key string) + AsMap() map[string]Property + } +)