diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 03f50d80..077e2529 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -@talsabagport @danielsinai @dvirsegev \ No newline at end of file +@talsabagport @danielsinai @dvirsegev @matarpeles \ No newline at end of file diff --git a/internal/cli/models.go b/internal/cli/models.go index eee34e5d..02320706 100644 --- a/internal/cli/models.go +++ b/internal/cli/models.go @@ -53,12 +53,51 @@ type ( EnumColors map[string]string `json:"enumColors,omitempty"` } + ActionProperty struct { + Type string `json:"type,omitempty"` + Title *string `json:"title,omitempty"` + Identifier string `json:"identifier,omitempty"` + Items map[string]any `json:"items,omitempty"` + Default interface{} `json:"default,omitempty"` + Icon *string `json:"icon,omitempty"` + Format *string `json:"format,omitempty"` + MaxLength *int `json:"maxLength,omitempty"` + MinLength *int `json:"minLength,omitempty"` + MaxItems *int `json:"maxItems,omitempty"` + MinItems *int `json:"minItems,omitempty"` + Maximum *float64 `json:"maximum,omitempty"` + Minimum *float64 `json:"minimum,omitempty"` + Description *string `json:"description,omitempty"` + Blueprint *string `json:"blueprint,omitempty"` + Pattern *string `json:"pattern,omitempty"` + Enum interface{} `json:"enum,omitempty"` + Spec *string `json:"spec,omitempty"` + SpecAuthentication *SpecAuthentication `json:"specAuthentication,omitempty"` + EnumColors map[string]string `json:"enumColors,omitempty"` + DependsOn []string `json:"dependsOn,omitempty"` + Dataset *Dataset `json:"dataset,omitempty"` + } + SpecAuthentication struct { ClientId string `json:"clientId,omitempty"` AuthorizationUrl string `json:"authorizationUrl,omitempty"` TokenUrl string `json:"tokenUrl,omitempty"` } + DatasetValue struct { + JqQuery string `json:"jqQuery,omitempty"` + } + DatasetRule struct { + Blueprint *string `json:"blueprint,omitempty"` + Property *string `json:"property,omitempty"` + Operator string `json:"operator,omitempty"` + Value *DatasetValue `json:"value,omitempty"` + } + Dataset struct { + Combinator string `json:"combinator,omitempty"` + Rules []DatasetRule `json:"rules,omitempty"` + } + BlueprintCalculationProperty struct { Type string `json:"type,omitempty"` Title *string `json:"title,omitempty"` @@ -95,6 +134,9 @@ type ( OmitUserInputs *bool `json:"omitUserInputs,omitempty"` ReportWorkflowStatus *bool `json:"reportWorkflowStatus,omitempty"` Branch *string `json:"branch,omitempty"` + ProjectName *string `json:"projectName,omitempty"` + GroupName *string `json:"groupName,omitempty"` + DefaultRef *string `json:"defaultRef,omitempty"` } ApprovalNotification struct { @@ -113,7 +155,10 @@ type ( Path string `json:"path,omitempty"` } - ActionUserInputs = BlueprintSchema + ActionUserInputs = struct { + Properties map[string]ActionProperty `json:"properties"` + Required []string `json:"required,omitempty"` + } Blueprint struct { Meta diff --git a/internal/consts/provider.go b/internal/consts/provider.go index 5e2ff152..c472f1eb 100644 --- a/internal/consts/provider.go +++ b/internal/consts/provider.go @@ -7,4 +7,5 @@ const ( Kafka = "KAFKA" AzureDevops = "AZURE-DEVOPS" Github = "GITHUB" + Gitlab = "GITLAB" ) diff --git a/internal/flex/string.go b/internal/flex/string.go index bec9f6fc..51f57a4c 100644 --- a/internal/flex/string.go +++ b/internal/flex/string.go @@ -1,7 +1,11 @@ package flex import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) func GoStringToFramework(v *string) types.String { @@ -11,3 +15,15 @@ func GoStringToFramework(v *string) types.String { return types.StringValue(*v) } + +func GoArrayStringToTerraformList(ctx context.Context, array []string) types.List { + if array == nil { + return types.ListNull(types.StringType) + } + attrs := make([]attr.Value, 0, len(array)) + for _, value := range array { + attrs = append(attrs, basetypes.NewStringValue(value)) + } + list, _ := types.ListValue(types.StringType, attrs) + return list +} diff --git a/internal/utils/utils.go b/internal/utils/utils.go index ff2a8208..6b01876f 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -84,3 +84,13 @@ func GoObjectToTerraformString(v interface{}) (types.String, error) { value := string(js) return types.StringValue(value), nil } + +func InterfaceToStringArray(o interface{}) []string { + items := o.([]interface{}) + res := make([]string, len(items)) + for i, item := range items { + res[i] = item.(string) + } + + return res +} diff --git a/port/action/actionStateToPortBody.go b/port/action/actionStateToPortBody.go index 9c7df72e..2e1abf6a 100644 --- a/port/action/actionStateToPortBody.go +++ b/port/action/actionStateToPortBody.go @@ -7,6 +7,33 @@ import ( "github.com/port-labs/terraform-provider-port-labs/internal/consts" ) +func actionDataSetToPortBody(dataSet *DatasetModel) *cli.Dataset { + cliDateSet := &cli.Dataset{ + Combinator: dataSet.Combinator.ValueString(), + } + rules := make([]cli.DatasetRule, 0, len(dataSet.Rules)) + for _, rule := range dataSet.Rules { + dataSetRule := cli.DatasetRule{ + Operator: rule.Operator.ValueString(), + Value: &cli.DatasetValue{ + JqQuery: rule.Value.JqQuery.ValueString(), + }, + } + if !rule.Blueprint.IsNull() { + blueprint := rule.Blueprint.ValueString() + dataSetRule.Blueprint = &blueprint + } + if !rule.Property.IsNull() { + rule := rule.Property.ValueString() + dataSetRule.Property = &rule + } + + rules = append(rules, dataSetRule) + } + cliDateSet.Rules = rules + return cliDateSet +} + func actionStateToPortBody(ctx context.Context, data *ActionModel, bp *cli.Blueprint) (*cli.Action, error) { action := &cli.Action{ Identifier: data.Identifier.ValueString(), @@ -55,7 +82,7 @@ func actionStateToPortBody(ctx context.Context, data *ActionModel, bp *cli.Bluep return nil, err } } else { - action.UserInputs.Properties = make(map[string]cli.BlueprintProperty) + action.UserInputs.Properties = make(map[string]cli.ActionProperty) } return action, nil @@ -63,7 +90,7 @@ func actionStateToPortBody(ctx context.Context, data *ActionModel, bp *cli.Bluep func actionPropertiesToBody(ctx context.Context, action *cli.Action, data *ActionModel) error { required := []string{} - props := map[string]cli.BlueprintProperty{} + props := map[string]cli.ActionProperty{} var err error if data.UserProperties.StringProps != nil { err = stringPropResourceToBody(ctx, data, props, &required) @@ -75,11 +102,11 @@ func actionPropertiesToBody(ctx context.Context, action *cli.Action, data *Actio err = numberPropResourceToBody(ctx, data, props, &required) } if data.UserProperties.BooleanProps != nil { - booleanPropResourceToBody(data, props, &required) + err = booleanPropResourceToBody(ctx, data, props, &required) } if data.UserProperties.ObjectProps != nil { - err = objectPropResourceToBody(data, props, &required) + err = objectPropResourceToBody(ctx, data, props, &required) } if err != nil { @@ -148,5 +175,38 @@ func invocationMethodToBody(data *ActionModel) *cli.InvocationMethod { } return webhookInvocation } + + if data.GitlabMethod != nil { + projectName := data.GitlabMethod.ProjectName.ValueString() + groupName := data.GitlabMethod.GroupName.ValueString() + gitlabInvocation := &cli.InvocationMethod{ + Type: consts.Gitlab, + ProjectName: &projectName, + GroupName: &groupName, + } + + if !data.GitlabMethod.OmitPayload.IsNull() { + omitPayload := data.GitlabMethod.OmitPayload.ValueBool() + gitlabInvocation.OmitPayload = &omitPayload + } + + if !data.GitlabMethod.OmitUserInputs.IsNull() { + omitUserInputs := data.GitlabMethod.OmitUserInputs.ValueBool() + gitlabInvocation.OmitUserInputs = &omitUserInputs + } + + if !data.GitlabMethod.DefaultRef.IsNull() { + defaultRef := data.GitlabMethod.DefaultRef.ValueString() + gitlabInvocation.DefaultRef = &defaultRef + } + + if !data.GitlabMethod.Agent.IsNull() { + agent := data.GitlabMethod.Agent.ValueBool() + gitlabInvocation.Agent = &agent + } + + return gitlabInvocation + } + return nil } diff --git a/port/action/array.go b/port/action/array.go index 0364c51b..bc01b983 100644 --- a/port/action/array.go +++ b/port/action/array.go @@ -12,7 +12,7 @@ import ( "github.com/port-labs/terraform-provider-port-labs/internal/utils" ) -func handleArrayItemsToBody(ctx context.Context, property *cli.BlueprintProperty, prop ArrayPropModel, required *[]string) error { +func handleArrayItemsToBody(ctx context.Context, property *cli.ActionProperty, prop ArrayPropModel, required *[]string) error { if prop.StringItems != nil { items := map[string]interface{}{} items["type"] = "string" @@ -86,9 +86,9 @@ func handleArrayItemsToBody(ctx context.Context, property *cli.BlueprintProperty return nil } -func arrayPropResourceToBody(ctx context.Context, d *ActionModel, props map[string]cli.BlueprintProperty, required *[]string) error { +func arrayPropResourceToBody(ctx context.Context, d *ActionModel, props map[string]cli.ActionProperty, required *[]string) error { for propIdentifier, prop := range d.UserProperties.ArrayProps { - props[propIdentifier] = cli.BlueprintProperty{ + props[propIdentifier] = cli.ActionProperty{ Type: "array", } @@ -104,6 +104,14 @@ func arrayPropResourceToBody(ctx context.Context, d *ActionModel, props map[stri property.Icon = &icon } + if !prop.DefaultJqQuery.IsNull() { + defaultJqQuery := prop.DefaultJqQuery.ValueString() + jqQueryMap := map[string]string{ + "jqQuery": defaultJqQuery, + } + property.Default = jqQueryMap + } + if !prop.Description.IsNull() { description := prop.Description.ValueString() property.Description = &description @@ -118,6 +126,18 @@ func arrayPropResourceToBody(ctx context.Context, d *ActionModel, props map[stri property.MaxItems = &maxItems } + if !prop.DependsOn.IsNull() { + dependsOn, err := utils.TerraformListToGoArray(ctx, prop.DependsOn, "string") + if err != nil { + return err + } + property.DependsOn = utils.InterfaceToStringArray(dependsOn) + + } + if prop.Dataset != nil { + property.Dataset = actionDataSetToPortBody(prop.Dataset) + } + err := handleArrayItemsToBody(ctx, &property, prop, required) if err != nil { return err @@ -133,12 +153,20 @@ func arrayPropResourceToBody(ctx context.Context, d *ActionModel, props map[stri return nil } -func addArrayPropertiesToResource(v *cli.BlueprintProperty) (*ArrayPropModel, error) { +func addArrayPropertiesToResource(v *cli.ActionProperty) (*ArrayPropModel, error) { arrayProp := &ArrayPropModel{ MinItems: flex.GoInt64ToFramework(v.MinItems), MaxItems: flex.GoInt64ToFramework(v.MaxItems), } + if v.Default != nil { + switch v := v.Default.(type) { + // We only test for map[string]interface{} ATM + case map[string]interface{}: + arrayProp.DefaultJqQuery = types.StringValue(v["jqQuery"].(string)) + } + } + if v.Items != nil { if v.Items["type"] != "" { switch v.Items["type"] { diff --git a/port/action/boolean.go b/port/action/boolean.go index f5c92554..43bac316 100644 --- a/port/action/boolean.go +++ b/port/action/boolean.go @@ -1,10 +1,15 @@ package action -import "github.com/port-labs/terraform-provider-port-labs/internal/cli" +import ( + "context" -func booleanPropResourceToBody(d *ActionModel, props map[string]cli.BlueprintProperty, required *[]string) { + "github.com/port-labs/terraform-provider-port-labs/internal/cli" + "github.com/port-labs/terraform-provider-port-labs/internal/utils" +) + +func booleanPropResourceToBody(ctx context.Context, d *ActionModel, props map[string]cli.ActionProperty, required *[]string) error { for propIdentifier, prop := range d.UserProperties.BooleanProps { - props[propIdentifier] = cli.BlueprintProperty{ + props[propIdentifier] = cli.ActionProperty{ Type: "boolean", } @@ -18,6 +23,14 @@ func booleanPropResourceToBody(d *ActionModel, props map[string]cli.BlueprintPro property.Default = prop.Default.ValueBool() } + if !prop.DefaultJqQuery.IsNull() { + defaultJqQuery := prop.DefaultJqQuery.ValueString() + jqQueryMap := map[string]string{ + "jqQuery": defaultJqQuery, + } + property.Default = jqQueryMap + } + if !prop.Icon.IsNull() { icon := prop.Icon.ValueString() property.Icon = &icon @@ -28,10 +41,23 @@ func booleanPropResourceToBody(d *ActionModel, props map[string]cli.BlueprintPro property.Description = &description } + if !prop.DependsOn.IsNull() { + dependsOn, err := utils.TerraformListToGoArray(ctx, prop.DependsOn, "string") + if err != nil { + return err + } + property.DependsOn = utils.InterfaceToStringArray(dependsOn) + + } + if prop.Dataset != nil { + property.Dataset = actionDataSetToPortBody(prop.Dataset) + } + props[propIdentifier] = property } if prop.Required.ValueBool() { *required = append(*required, propIdentifier) } } + return nil } diff --git a/port/action/model.go b/port/action/model.go index 87529567..f788412e 100644 --- a/port/action/model.go +++ b/port/action/model.go @@ -9,6 +9,20 @@ type WebhookMethodModel struct { Agent types.Bool `tfsdk:"agent"` } +type Value struct { + JqQuery types.String `tfsdk:"jq_query"` +} +type Rule struct { + Blueprint types.String `tfsdk:"blueprint"` + Property types.String `tfsdk:"property"` + Operator types.String `tfsdk:"operator"` + Value *Value `tfsdk:"value"` +} +type DatasetModel struct { + Combinator types.String `tfsdk:"combinator"` + Rules []Rule `tfsdk:"rules"` +} + type GithubMethodModel struct { Org types.String `tfsdk:"org"` Repo types.String `tfsdk:"repo"` @@ -23,37 +37,84 @@ type AzureMethodModel struct { Webhook types.String `tfsdk:"webhook"` } +type GitlabMethodModel struct { + ProjectName types.String `tfsdk:"project_name"` + GroupName types.String `tfsdk:"group_name"` + OmitPayload types.Bool `tfsdk:"omit_payload"` + OmitUserInputs types.Bool `tfsdk:"omit_user_inputs"` + DefaultRef types.String `tfsdk:"default_ref"` + Agent types.Bool `tfsdk:"agent"` +} + type StringPropModel struct { - Title types.String `tfsdk:"title"` - Icon types.String `tfsdk:"icon"` - Blueprint types.String `tfsdk:"blueprint"` - Description types.String `tfsdk:"description"` - Default types.String `tfsdk:"default"` - Required types.Bool `tfsdk:"required"` - Format types.String `tfsdk:"format"` - MaxLength types.Int64 `tfsdk:"max_length"` - MinLength types.Int64 `tfsdk:"min_length"` - Pattern types.String `tfsdk:"pattern"` - Enum types.List `tfsdk:"enum"` + Title types.String `tfsdk:"title"` + Icon types.String `tfsdk:"icon"` + Blueprint types.String `tfsdk:"blueprint"` + Description types.String `tfsdk:"description"` + Default types.String `tfsdk:"default"` + Required types.Bool `tfsdk:"required"` + Format types.String `tfsdk:"format"` + MaxLength types.Int64 `tfsdk:"max_length"` + MinLength types.Int64 `tfsdk:"min_length"` + Pattern types.String `tfsdk:"pattern"` + Enum types.List `tfsdk:"enum"` + DependsOn types.List `tfsdk:"depends_on"` + Dataset *DatasetModel `tfsdk:"dataset"` + DefaultJqQuery types.String `tfsdk:"default_jq_query"` + EnumJqQuery types.String `tfsdk:"enum_jq_query"` } type NumberPropModel struct { - Title types.String `tfsdk:"title"` - Icon types.String `tfsdk:"icon"` - Description types.String `tfsdk:"description"` - Default types.Float64 `tfsdk:"default"` - Required types.Bool `tfsdk:"required"` - Maximum types.Float64 `tfsdk:"maximum"` - Minimum types.Float64 `tfsdk:"minimum"` - Enum types.List `tfsdk:"enum"` + Title types.String `tfsdk:"title"` + Icon types.String `tfsdk:"icon"` + Description types.String `tfsdk:"description"` + Default types.Float64 `tfsdk:"default"` + Required types.Bool `tfsdk:"required"` + Maximum types.Float64 `tfsdk:"maximum"` + Minimum types.Float64 `tfsdk:"minimum"` + Enum types.List `tfsdk:"enum"` + DependsOn types.List `tfsdk:"depends_on"` + Dataset *DatasetModel `tfsdk:"dataset"` + DefaultJqQuery types.String `tfsdk:"default_jq_query"` + EnumJqQuery types.String `tfsdk:"enum_jq_query"` } type BooleanPropModel struct { - Title types.String `tfsdk:"title"` - Icon types.String `tfsdk:"icon"` - Description types.String `tfsdk:"description"` - Default types.Bool `tfsdk:"default"` - Required types.Bool `tfsdk:"required"` + Title types.String `tfsdk:"title"` + Icon types.String `tfsdk:"icon"` + Description types.String `tfsdk:"description"` + Default types.Bool `tfsdk:"default"` + Required types.Bool `tfsdk:"required"` + DependsOn types.List `tfsdk:"depends_on"` + Dataset *DatasetModel `tfsdk:"dataset"` + DefaultJqQuery types.String `tfsdk:"default_jq_query"` +} + +type ArrayPropModel struct { + Title types.String `tfsdk:"title"` + Icon types.String `tfsdk:"icon"` + Description types.String `tfsdk:"description"` + MaxItems types.Int64 `tfsdk:"max_items"` + MinItems types.Int64 `tfsdk:"min_items"` + Required types.Bool `tfsdk:"required"` + StringItems *StringItems `tfsdk:"string_items"` + NumberItems *NumberItems `tfsdk:"number_items"` + BooleanItems *BooleanItems `tfsdk:"boolean_items"` + ObjectItems *ObjectItems `tfsdk:"object_items"` + DependsOn types.List `tfsdk:"depends_on"` + Dataset *DatasetModel `tfsdk:"dataset"` + DefaultJqQuery types.String `tfsdk:"default_jq_query"` +} + +type ObjectPropModel struct { + Title types.String `tfsdk:"title"` + Icon types.String `tfsdk:"icon"` + Description types.String `tfsdk:"description"` + Required types.Bool `tfsdk:"required"` + Default types.String `tfsdk:"default"` + DependsOn types.List `tfsdk:"depends_on"` + Dataset *DatasetModel `tfsdk:"dataset"` + DefaultJqQuery types.String `tfsdk:"default_jq_query"` } type StringItems struct { @@ -82,27 +143,6 @@ type UserPropertiesModel struct { ObjectProps map[string]ObjectPropModel `tfsdk:"object_props"` } -type ArrayPropModel struct { - Title types.String `tfsdk:"title"` - Icon types.String `tfsdk:"icon"` - Description types.String `tfsdk:"description"` - MaxItems types.Int64 `tfsdk:"max_items"` - MinItems types.Int64 `tfsdk:"min_items"` - Required types.Bool `tfsdk:"required"` - StringItems *StringItems `tfsdk:"string_items"` - NumberItems *NumberItems `tfsdk:"number_items"` - BooleanItems *BooleanItems `tfsdk:"boolean_items"` - ObjectItems *ObjectItems `tfsdk:"object_items"` -} - -type ObjectPropModel struct { - Title types.String `tfsdk:"title"` - Icon types.String `tfsdk:"icon"` - Description types.String `tfsdk:"description"` - Required types.Bool `tfsdk:"required"` - Default types.String `tfsdk:"default"` -} - type ApprovalWebhookNotificationModel struct { Url types.String `tfsdk:"url"` Format types.String `tfsdk:"format"` @@ -121,6 +161,7 @@ type ActionModel struct { WebhookMethod *WebhookMethodModel `tfsdk:"webhook_method"` GithubMethod *GithubMethodModel `tfsdk:"github_method"` AzureMethod *AzureMethodModel `tfsdk:"azure_method"` + GitlabMethod *GitlabMethodModel `tfsdk:"gitlab_method"` UserProperties *UserPropertiesModel `tfsdk:"user_properties"` ApprovalWebhookNotification *ApprovalWebhookNotificationModel `tfsdk:"approval_webhook_notification"` ApprovalEmailNotification types.Object `tfsdk:"approval_email_notification"` diff --git a/port/action/number.go b/port/action/number.go index 55c1e812..bcbd1b72 100644 --- a/port/action/number.go +++ b/port/action/number.go @@ -2,6 +2,7 @@ package action import ( "context" + "reflect" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" @@ -11,9 +12,9 @@ import ( "github.com/port-labs/terraform-provider-port-labs/internal/utils" ) -func numberPropResourceToBody(ctx context.Context, state *ActionModel, props map[string]cli.BlueprintProperty, required *[]string) error { +func numberPropResourceToBody(ctx context.Context, state *ActionModel, props map[string]cli.ActionProperty, required *[]string) error { for propIdentifier, prop := range state.UserProperties.NumberProps { - props[propIdentifier] = cli.BlueprintProperty{ + props[propIdentifier] = cli.ActionProperty{ Type: "number", } @@ -27,6 +28,14 @@ func numberPropResourceToBody(ctx context.Context, state *ActionModel, props map property.Default = prop.Default.ValueFloat64() } + if !prop.DefaultJqQuery.IsNull() { + defaultJqQuery := prop.DefaultJqQuery.ValueString() + jqQueryMap := map[string]string{ + "jqQuery": defaultJqQuery, + } + property.Default = jqQueryMap + } + if !prop.Icon.IsNull() { icon := prop.Icon.ValueString() property.Icon = &icon @@ -56,6 +65,19 @@ func numberPropResourceToBody(ctx context.Context, state *ActionModel, props map property.Enum = enumList } + if !prop.DependsOn.IsNull() { + dependsOn, err := utils.TerraformListToGoArray(ctx, prop.DependsOn, "string") + if err != nil { + return err + } + property.DependsOn = utils.InterfaceToStringArray(dependsOn) + + } + + if prop.Dataset != nil { + property.Dataset = actionDataSetToPortBody(prop.Dataset) + } + props[propIdentifier] = property } if prop.Required.ValueBool() { @@ -65,19 +87,30 @@ func numberPropResourceToBody(ctx context.Context, state *ActionModel, props map return nil } -func addNumberPropertiesToResource(ctx context.Context, v *cli.BlueprintProperty) *NumberPropModel { +func addNumberPropertiesToResource(ctx context.Context, v *cli.ActionProperty) *NumberPropModel { numberProp := &NumberPropModel{ Minimum: flex.GoFloat64ToFramework(v.Minimum), Maximum: flex.GoFloat64ToFramework(v.Maximum), } if v.Enum != nil { - attrs := make([]attr.Value, 0, len(v.Enum)) - for _, value := range v.Enum { - attrs = append(attrs, basetypes.NewFloat64Value(value.(float64))) - } + v := reflect.ValueOf(v.Enum) + switch v.Kind() { + case reflect.Slice: + slice := v.Interface().([]interface{}) + attrs := make([]attr.Value, 0, v.Len()) + for _, value := range slice { + attrs = append(attrs, basetypes.NewFloat64Value(value.(float64))) + } + + numberProp.Enum, _ = types.ListValue(types.Float64Type, attrs) - numberProp.Enum, _ = types.ListValue(types.Float64Type, attrs) + case reflect.Map: + v := v.Interface().(map[string]interface{}) + jqQueryValue := v["jqQuery"].(string) + numberProp.EnumJqQuery = flex.GoStringToFramework(&jqQueryValue) + numberProp.Enum = types.ListNull(types.StringType) + } } else { numberProp.Enum = types.ListNull(types.Float64Type) } diff --git a/port/action/object.go b/port/action/object.go index ddcabc33..b65b7d92 100644 --- a/port/action/object.go +++ b/port/action/object.go @@ -1,14 +1,16 @@ package action import ( + "context" "encoding/json" "github.com/port-labs/terraform-provider-port-labs/internal/cli" + "github.com/port-labs/terraform-provider-port-labs/internal/utils" ) -func objectPropResourceToBody(d *ActionModel, props map[string]cli.BlueprintProperty, required *[]string) error { +func objectPropResourceToBody(ctx context.Context, d *ActionModel, props map[string]cli.ActionProperty, required *[]string) error { for propIdentifier, prop := range d.UserProperties.ObjectProps { - props[propIdentifier] = cli.BlueprintProperty{ + props[propIdentifier] = cli.ActionProperty{ Type: "object", } @@ -24,6 +26,14 @@ func objectPropResourceToBody(d *ActionModel, props map[string]cli.BlueprintProp } } + if !prop.DefaultJqQuery.IsNull() { + defaultJqQuery := prop.DefaultJqQuery.ValueString() + jqQueryMap := map[string]string{ + "jqQuery": defaultJqQuery, + } + property.Default = jqQueryMap + } + if !prop.Title.IsNull() { title := prop.Title.ValueString() property.Title = &title @@ -39,6 +49,19 @@ func objectPropResourceToBody(d *ActionModel, props map[string]cli.BlueprintProp property.Description = &description } + if !prop.DependsOn.IsNull() { + dependsOn, err := utils.TerraformListToGoArray(ctx, prop.DependsOn, "string") + if err != nil { + return err + } + property.DependsOn = utils.InterfaceToStringArray(dependsOn) + + } + + if prop.Dataset != nil { + property.Dataset = actionDataSetToPortBody(prop.Dataset) + } + props[propIdentifier] = property } @@ -49,7 +72,7 @@ func objectPropResourceToBody(d *ActionModel, props map[string]cli.BlueprintProp return nil } -func addObjectPropertiesToResource(v *cli.BlueprintProperty) *ObjectPropModel { +func addObjectPropertiesToResource(v *cli.ActionProperty) *ObjectPropModel { objectProp := &ObjectPropModel{} return objectProp diff --git a/port/action/refreshActionState.go b/port/action/refreshActionState.go index 2dc4296e..026d4c5b 100644 --- a/port/action/refreshActionState.go +++ b/port/action/refreshActionState.go @@ -41,8 +41,45 @@ func writeInvocationMethodToResource(a *cli.Action, state *ActionModel) { Webhook: types.StringValue(*a.InvocationMethod.Webhook), } } + + if a.InvocationMethod.Type == consts.Gitlab { + state.GitlabMethod = &GitlabMethodModel{ + ProjectName: types.StringValue(*a.InvocationMethod.ProjectName), + GroupName: types.StringValue(*a.InvocationMethod.GroupName), + OmitPayload: flex.GoBoolToFramework(a.InvocationMethod.OmitPayload), + OmitUserInputs: flex.GoBoolToFramework(a.InvocationMethod.OmitUserInputs), + DefaultRef: types.StringValue(*a.InvocationMethod.DefaultRef), + Agent: flex.GoBoolToFramework(a.InvocationMethod.Agent), + } + } } +func writeDatasetToResource(v cli.ActionProperty) *DatasetModel { + if v.Dataset == nil { + return nil + } + + dataset := v.Dataset + + datasetModel := &DatasetModel{ + Combinator: types.StringValue(dataset.Combinator), + } + + for _, v := range dataset.Rules { + rule := &Rule{ + Blueprint: flex.GoStringToFramework(v.Blueprint), + Property: flex.GoStringToFramework(v.Property), + Operator: flex.GoStringToFramework(&v.Operator), + Value: &Value{ + JqQuery: flex.GoStringToFramework(&v.Value.JqQuery), + }, + } + datasetModel.Rules = append(datasetModel.Rules, *rule) + } + + return datasetModel + +} func writeInputsToResource(ctx context.Context, a *cli.Action, state *ActionModel) error { if len(a.UserInputs.Properties) > 0 { properties := &UserPropertiesModel{} @@ -60,7 +97,7 @@ func writeInputsToResource(ctx context.Context, a *cli.Action, state *ActionMode stringProp.Required = types.BoolValue(false) } - err := setCommonProperties(v, stringProp) + err := setCommonProperties(ctx, v, stringProp) if err != nil { return err } @@ -80,7 +117,7 @@ func writeInputsToResource(ctx context.Context, a *cli.Action, state *ActionMode numberProp.Required = types.BoolValue(false) } - err := setCommonProperties(v, numberProp) + err := setCommonProperties(ctx, v, numberProp) if err != nil { return err } @@ -103,7 +140,7 @@ func writeInputsToResource(ctx context.Context, a *cli.Action, state *ActionMode arrayProp.Required = types.BoolValue(false) } - err = setCommonProperties(v, arrayProp) + err = setCommonProperties(ctx, v, arrayProp) if err != nil { return err } @@ -117,7 +154,7 @@ func writeInputsToResource(ctx context.Context, a *cli.Action, state *ActionMode booleanProp := &BooleanPropModel{} - err := setCommonProperties(v, booleanProp) + err := setCommonProperties(ctx, v, booleanProp) if err != nil { return err } @@ -143,7 +180,7 @@ func writeInputsToResource(ctx context.Context, a *cli.Action, state *ActionMode objectProp.Required = types.BoolValue(false) } - err := setCommonProperties(v, objectProp) + err := setCommonProperties(ctx, v, objectProp) if err != nil { return err } @@ -192,8 +229,8 @@ func refreshActionState(ctx context.Context, state *ActionModel, a *cli.Action, return nil } -func setCommonProperties(v cli.BlueprintProperty, prop interface{}) error { - properties := []string{"Description", "Icon", "Default", "Title"} +func setCommonProperties(ctx context.Context, v cli.ActionProperty, prop interface{}) error { + properties := []string{"Description", "Icon", "Default", "Title", "DependsOn", "Dataset"} for _, property := range properties { switch property { case "Description": @@ -241,27 +278,85 @@ func setCommonProperties(v cli.BlueprintProperty, prop interface{}) error { case *StringPropModel: if v.Default == nil { p.Default = types.StringNull() + p.DefaultJqQuery = types.StringNull() } else { - p.Default = types.StringValue(v.Default.(string)) + switch v := v.Default.(type) { + case string: + p.Default = types.StringValue(v) + case map[string]interface{}: + p.DefaultJqQuery = types.StringValue(v["jqQuery"].(string)) + } } case *NumberPropModel: if v.Default == nil { p.Default = types.Float64Null() + p.DefaultJqQuery = types.StringNull() } else { - p.Default = types.Float64Value(v.Default.(float64)) + switch v := v.Default.(type) { + case float64: + p.Default = types.Float64Value(v) + case map[string]interface{}: + p.DefaultJqQuery = types.StringValue(v["jqQuery"].(string)) + } } case *BooleanPropModel: if v.Default == nil { p.Default = types.BoolNull() + p.DefaultJqQuery = types.StringNull() } else { - p.Default = types.BoolValue(v.Default.(bool)) + switch v := v.Default.(type) { + case bool: + p.Default = types.BoolValue(v) + case map[string]interface{}: + p.DefaultJqQuery = types.StringValue(v["jqQuery"].(string)) + } } case *ObjectPropModel: - defaultValue, err := utils.GoObjectToTerraformString(v.Default) - if err != nil { - return fmt.Errorf("error converting default value to terraform string: %s", err.Error()) + if v, ok := v.Default.(map[string]interface{}); ok { + if v["jqQuery"] != nil { + p.DefaultJqQuery = types.StringValue(v["jqQuery"].(string)) + } else { + defaultValue, err := utils.GoObjectToTerraformString(v) + if err != nil { + return fmt.Errorf("error converting default value to terraform string: %s", err.Error()) + } + if defaultValue.IsNull() { + p.Default = types.StringNull() + p.DefaultJqQuery = types.StringNull() + } + p.Default = defaultValue + } + } + } + case "DependsOn": + switch p := prop.(type) { + case *StringPropModel: + p.DependsOn = flex.GoArrayStringToTerraformList(ctx, v.DependsOn) + case *NumberPropModel: + p.DependsOn = flex.GoArrayStringToTerraformList(ctx, v.DependsOn) + case *BooleanPropModel: + p.DependsOn = flex.GoArrayStringToTerraformList(ctx, v.DependsOn) + case *ArrayPropModel: + p.DependsOn = flex.GoArrayStringToTerraformList(ctx, v.DependsOn) + case *ObjectPropModel: + p.DependsOn = flex.GoArrayStringToTerraformList(ctx, v.DependsOn) + } + + case "Dataset": + dataset := writeDatasetToResource(v) + if dataset != nil { + switch p := prop.(type) { + case *StringPropModel: + p.Dataset = dataset + case *NumberPropModel: + p.Dataset = dataset + case *BooleanPropModel: + p.Dataset = dataset + case *ArrayPropModel: + p.Dataset = dataset + case *ObjectPropModel: + p.Dataset = dataset } - p.Default = defaultValue } } } diff --git a/port/action/resource_test.go b/port/action/resource_test.go index e47c4353..bcd64583 100644 --- a/port/action/resource_test.go +++ b/port/action/resource_test.go @@ -175,6 +175,49 @@ func TestAccPortActionWebhookInvocation(t *testing.T) { }) } +func TestAccPortActionGitlabInvocation(t *testing.T) { + identifier := utils.GenID() + actionIdentifier := utils.GenID() + var testAccActionConfigCreate = testAccCreateBlueprintConfig(identifier) + fmt.Sprintf(` + resource "port_action" "create_microservice" { + title = "TF Provider Test" + identifier = "%s" + icon = "Terraform" + blueprint = port_blueprint.microservice.id + trigger = "DAY-2" + gitlab_method = { + project_name = "terraform-provider-port" + group_name = "port" + omit_payload = true + omit_user_inputs = true + default_ref = "main" + agent = true + } + }`, actionIdentifier) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: acctest.ProviderConfig + testAccActionConfigCreate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_action.create_microservice", "title", "TF Provider Test"), + resource.TestCheckResourceAttr("port_action.create_microservice", "identifier", actionIdentifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "icon", "Terraform"), + resource.TestCheckResourceAttr("port_action.create_microservice", "blueprint", identifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "trigger", "DAY-2"), + resource.TestCheckResourceAttr("port_action.create_microservice", "gitlab_method.project_name", "terraform-provider-port"), + resource.TestCheckResourceAttr("port_action.create_microservice", "gitlab_method.group_name", "port"), + resource.TestCheckResourceAttr("port_action.create_microservice", "gitlab_method.omit_payload", "true"), + resource.TestCheckResourceAttr("port_action.create_microservice", "gitlab_method.omit_user_inputs", "true"), + resource.TestCheckResourceAttr("port_action.create_microservice", "gitlab_method.default_ref", "main"), + resource.TestCheckResourceAttr("port_action.create_microservice", "gitlab_method.agent", "true"), + ), + }, + }, + }) +} func TestAccPortActionAzureInvocation(t *testing.T) { identifier := utils.GenID() actionIdentifier := utils.GenID() @@ -383,3 +426,162 @@ func TestAccPortActionUpdate(t *testing.T) { }, }) } + +func TestAccPortActionAdvancedFormConfigurations(t *testing.T) { + identifier := utils.GenID() + actionIdentifier := utils.GenID() + var testAccActionConfigCreate = testAccCreateBlueprintConfig(identifier) + fmt.Sprintf(` + resource "port_action" "action1" { + title = "Action 1" + blueprint = port_blueprint.microservice.id + identifier = "%s" + trigger = "DAY-2" + description = "This is a test action" + required_approval = true + github_method = { + org = "port-labs" + repo = "Port" + workflow = "lint" + } + user_properties = { + string_props = { + myStringIdentifier = { + title = "myStringIdentifier" + default = "default" + required = false + } + myStringIdentifier2 = { + title = "myStringIdentifier2" + default = "default" + required = false + depends_on = ["myStringIdentifier"] + } + myStringIdentifier3 = { + title = "myStringIdentifier3" + required = false + dataset = { + "combinator" : "and", + "rules" : [ + { + "property" : "$team", + "operator" : "containsAny", + "value" : { + "jq_query" : "Test" + } + } + ] + } + } + } + } + }`, actionIdentifier) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + + Steps: []resource.TestStep{ + { + Config: acctest.ProviderConfig + testAccActionConfigCreate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_action.action1", "title", "Action 1"), + resource.TestCheckResourceAttr("port_action.action1", "identifier", actionIdentifier), + resource.TestCheckResourceAttr("port_action.action1", "trigger", "DAY-2"), + resource.TestCheckResourceAttr("port_action.action1", "description", "This is a test action"), + resource.TestCheckResourceAttr("port_action.action1", "required_approval", "true"), + resource.TestCheckResourceAttr("port_action.action1", "github_method.org", "port-labs"), + resource.TestCheckResourceAttr("port_action.action1", "github_method.repo", "Port"), + resource.TestCheckResourceAttr("port_action.action1", "github_method.workflow", "lint"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier.title", "myStringIdentifier"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier.default", "default"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier.required", "false"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier2.title", "myStringIdentifier2"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier2.default", "default"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier2.required", "false"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier2.depends_on.0", "myStringIdentifier"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier3.title", "myStringIdentifier3"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier3.required", "false"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier3.dataset.combinator", "and"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier3.dataset.rules.0.property", "$team"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier3.dataset.rules.0.operator", "containsAny"), + resource.TestCheckResourceAttr("port_action.action1", "user_properties.string_props.myStringIdentifier3.dataset.rules.0.value.jq_query", "Test"), + ), + }, + }, + }) +} + +func TestAccPortActionJqDefault(t *testing.T) { + identifier := utils.GenID() + actionIdentifier := utils.GenID() + var testAccActionConfigCreate = testAccCreateBlueprintConfig(identifier) + fmt.Sprintf(` + resource "port_action" "create_microservice" { + title = "Action 1" + blueprint = port_blueprint.microservice.id + identifier = "%s" + trigger = "DAY-2" + description = "This is a test action" + kafka_method = {} + user_properties = { + string_props = { + myStringIdentifier = { + title = "myStringIdentifier" + default_jq_query = "'Test'" + required = false + } + } + number_props = { + myNumberIdentifier = { + title = "myNumberIdentifier" + default_jq_query = "1" + } + } + boolean_props = { + myBooleanIdentifier = { + title = "myBooleanIdentifier" + default_jq_query = "true" + } + } + object_props = { + myObjectIdentifier = { + title = "myObjectIdentifier" + default_jq_query = "{ \"test\": \"test\" }" + } + } + array_props = { + myArrayIdentifier = { + title = "myArrayIdentifier" + default_jq_query = "[ \"test\" ]" + } + } + } + }`, actionIdentifier) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + + Steps: []resource.TestStep{ + { + Config: acctest.ProviderConfig + testAccActionConfigCreate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_action.create_microservice", "title", "Action 1"), + resource.TestCheckResourceAttr("port_action.create_microservice", "identifier", actionIdentifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "trigger", "DAY-2"), + resource.TestCheckResourceAttr("port_action.create_microservice", "description", "This is a test action"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.string_props.myStringIdentifier.title", "myStringIdentifier"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.string_props.myStringIdentifier.default_jq_query", "'Test'"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.number_props.myNumberIdentifier.title", "myNumberIdentifier"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.number_props.myNumberIdentifier.default_jq_query", "1"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.boolean_props.myBooleanIdentifier.title", "myBooleanIdentifier"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.boolean_props.myBooleanIdentifier.default_jq_query", "true"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.object_props.myObjectIdentifier.title", "myObjectIdentifier"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.object_props.myObjectIdentifier.default_jq_query", "{ \"test\": \"test\" }"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.array_props.myArrayIdentifier.title", "myArrayIdentifier"), + resource.TestCheckResourceAttr("port_action.create_microservice", "user_properties.array_props.myArrayIdentifier.default_jq_query", "[ \"test\" ]"), + ), + }, + }, + }) + +} diff --git a/port/action/schema.go b/port/action/schema.go index 49a32ac9..4242ab09 100644 --- a/port/action/schema.go +++ b/port/action/schema.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" @@ -35,6 +36,51 @@ func MetadataProperties() map[string]schema.Attribute { MarkdownDescription: "The description of the property", Optional: true, }, + "depends_on": schema.ListAttribute{ + MarkdownDescription: "The properties that this property depends on", + Optional: true, + ElementType: types.StringType, + }, + "dataset": schema.SingleNestedAttribute{ + MarkdownDescription: "The dataset of the property", + Optional: true, + Attributes: map[string]schema.Attribute{ + "combinator": schema.StringAttribute{ + MarkdownDescription: "The combinator of the dataset", + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf("and", "or"), + }, + }, + "rules": schema.ListNestedAttribute{ + MarkdownDescription: "The rules of the dataset", + Required: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "blueprint": schema.StringAttribute{ + MarkdownDescription: "The blueprint identifier of the rule", + Optional: true, + }, + "property": schema.StringAttribute{ + MarkdownDescription: "The property identifier of the rule", + Optional: true, + }, + "operator": schema.StringAttribute{ + MarkdownDescription: "The operator of the rule", + Required: true, + }, + "value": schema.ObjectAttribute{ + MarkdownDescription: "The value of the rule", + Required: true, + AttributeTypes: map[string]attr.Type{ + "jq_query": types.StringType, + }, + }, + }, + }, + }, + }, + }, } } @@ -138,6 +184,38 @@ func ActionSchema() map[string]schema.Attribute { }, }, }, + "gitlab_method": schema.SingleNestedAttribute{ + MarkdownDescription: "The invocation method of the action", + Optional: true, + Attributes: map[string]schema.Attribute{ + "project_name": schema.StringAttribute{ + MarkdownDescription: "Required when selecting type GITLAB. The GitLab project name that the workflow belongs to", + Required: true, + }, + "group_name": schema.StringAttribute{ + MarkdownDescription: "Required when selecting type GITLAB. The GitLab group name that the workflow belongs to", + Required: true, + }, + "omit_payload": schema.BoolAttribute{ + MarkdownDescription: "Omit the payload when invoking the action", + Optional: true, + }, + "omit_user_inputs": schema.BoolAttribute{ + MarkdownDescription: "Omit the user inputs when invoking the action", + Optional: true, + }, + "default_ref": schema.StringAttribute{ + MarkdownDescription: "The default ref of the action", + Optional: true, + }, + "agent": schema.BoolAttribute{ + MarkdownDescription: "Use the agent to invoke the action", + Optional: true, + Computed: true, + Default: booldefault.StaticBool(true), + }, + }, + }, "azure_method": schema.SingleNestedAttribute{ MarkdownDescription: "The invocation method of the action", Optional: true, @@ -173,6 +251,13 @@ func StringPropertySchema() schema.Attribute { MarkdownDescription: "The default of the string property", Optional: true, }, + "default_jq_query": schema.StringAttribute{ + MarkdownDescription: "The default jq query of the string property", + Optional: true, + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("default")), + }, + }, "blueprint": schema.StringAttribute{ MarkdownDescription: "The blueprint identifier the string property relates to", Optional: true, @@ -208,6 +293,10 @@ func StringPropertySchema() schema.Attribute { listvalidator.SizeAtLeast(1), }, }, + "enum_jq_query": schema.StringAttribute{ + MarkdownDescription: "The enum jq query of the string property", + Optional: true, + }, } utils.CopyMaps(stringPropertySchema, MetadataProperties()) @@ -226,6 +315,13 @@ func NumberPropertySchema() schema.Attribute { MarkdownDescription: "The default of the number property", Optional: true, }, + "default_jq_query": schema.StringAttribute{ + MarkdownDescription: "The default jq query of the number property", + Optional: true, + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("default")), + }, + }, "maximum": schema.Float64Attribute{ MarkdownDescription: "The min of the number property", Optional: true, @@ -243,6 +339,10 @@ func NumberPropertySchema() schema.Attribute { listvalidator.SizeAtLeast(1), }, }, + "enum_jq_query": schema.StringAttribute{ + MarkdownDescription: "The enum jq query of the string property", + Optional: true, + }, } utils.CopyMaps(numberPropertySchema, MetadataProperties()) @@ -261,6 +361,13 @@ func BooleanPropertySchema() schema.Attribute { MarkdownDescription: "The default of the boolean property", Optional: true, }, + "default_jq_query": schema.StringAttribute{ + MarkdownDescription: "The default jq query of the boolean property", + Optional: true, + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("default")), + }, + }, } utils.CopyMaps(booleanPropertySchema, MetadataProperties()) @@ -279,6 +386,13 @@ func ObjectPropertySchema() schema.Attribute { Optional: true, MarkdownDescription: "The default of the object property", }, + "default_jq_query": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "The default jq query of the object property", + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("default")), + }, + }, } utils.CopyMaps(objectPropertySchema, MetadataProperties()) return schema.MapNestedAttribute{ @@ -306,6 +420,16 @@ func ArrayPropertySchema() schema.Attribute { int64validator.AtLeast(0), }, }, + "default_jq_query": schema.StringAttribute{ + MarkdownDescription: "The default jq query of the array property", + Optional: true, + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("string_items").AtName("default")), + stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("number_items").AtName("default")), + stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("boolean_items").AtName("default")), + stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("object_items").AtName("default")), + }, + }, "string_items": schema.SingleNestedAttribute{ MarkdownDescription: "The items of the array property", Optional: true, diff --git a/port/action/string.go b/port/action/string.go index cb9b514e..fa64e9f9 100644 --- a/port/action/string.go +++ b/port/action/string.go @@ -2,6 +2,7 @@ package action import ( "context" + "reflect" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" @@ -11,9 +12,9 @@ import ( "github.com/port-labs/terraform-provider-port-labs/internal/utils" ) -func stringPropResourceToBody(ctx context.Context, d *ActionModel, props map[string]cli.BlueprintProperty, required *[]string) error { +func stringPropResourceToBody(ctx context.Context, d *ActionModel, props map[string]cli.ActionProperty, required *[]string) error { for propIdentifier, prop := range d.UserProperties.StringProps { - property := cli.BlueprintProperty{ + property := cli.ActionProperty{ Type: "string", } @@ -26,6 +27,14 @@ func stringPropResourceToBody(ctx context.Context, d *ActionModel, props map[str property.Default = prop.Default.ValueString() } + if !prop.DefaultJqQuery.IsNull() { + defaultJqQuery := prop.DefaultJqQuery.ValueString() + jqQueryMap := map[string]string{ + "jqQuery": defaultJqQuery, + } + property.Default = jqQueryMap + } + if !prop.Format.IsNull() { format := prop.Format.ValueString() property.Format = &format @@ -70,6 +79,25 @@ func stringPropResourceToBody(ctx context.Context, d *ActionModel, props map[str property.Enum = enumList } + if !prop.EnumJqQuery.IsNull() { + enumJqQueryMap := map[string]string{ + "jqQuery": prop.EnumJqQuery.ValueString(), + } + property.Enum = enumJqQueryMap + } + + if !prop.DependsOn.IsNull() { + dependsOn, err := utils.TerraformListToGoArray(ctx, prop.DependsOn, "string") + if err != nil { + return err + } + property.DependsOn = utils.InterfaceToStringArray(dependsOn) + } + + if prop.Dataset != nil { + property.Dataset = actionDataSetToPortBody(prop.Dataset) + } + props[propIdentifier] = property if prop.Required.ValueBool() { @@ -79,7 +107,7 @@ func stringPropResourceToBody(ctx context.Context, d *ActionModel, props map[str return nil } -func addStringPropertiesToResource(ctx context.Context, v *cli.BlueprintProperty) *StringPropModel { +func addStringPropertiesToResource(ctx context.Context, v *cli.ActionProperty) *StringPropModel { stringProp := &StringPropModel{ MinLength: flex.GoInt64ToFramework(v.MinLength), MaxLength: flex.GoInt64ToFramework(v.MaxLength), @@ -89,14 +117,26 @@ func addStringPropertiesToResource(ctx context.Context, v *cli.BlueprintProperty } if v.Enum != nil { - attrs := make([]attr.Value, 0, len(v.Enum)) - for _, value := range v.Enum { - attrs = append(attrs, basetypes.NewStringValue(value.(string))) - } + v := reflect.ValueOf(v.Enum) + switch v.Kind() { + case reflect.Slice: + slice := v.Interface().([]interface{}) + attrs := make([]attr.Value, 0, v.Len()) + for _, value := range slice { + attrs = append(attrs, basetypes.NewStringValue(value.(string))) + } + stringProp.Enum, _ = types.ListValue(types.StringType, attrs) - stringProp.Enum, _ = types.ListValue(types.StringType, attrs) + case reflect.Map: + v := v.Interface().(map[string]interface{}) + jqQueryValue := v["jqQuery"].(string) + stringProp.EnumJqQuery = flex.GoStringToFramework(&jqQueryValue) + stringProp.Enum = types.ListNull(types.StringType) + + } } else { stringProp.Enum = types.ListNull(types.StringType) + stringProp.EnumJqQuery = types.StringNull() } return stringProp