diff --git a/.changelog/361.txt b/.changelog/361.txt new file mode 100644 index 00000000..94fe1371 --- /dev/null +++ b/.changelog/361.txt @@ -0,0 +1,23 @@ +```release-note:bug +`resource/davinci_variable`: Fixed panic crash when attempting to create a new flow variable that does not already exist. +``` + +```release-note:bug +`resource/davinci_variable`: Fixed "Error reading variable: json: cannot unmarshal object into Go struct field" error on all variables when a flow sets a flow variable value to an object type. +``` + +```release-note:breaking-change +`resource/davinci_flow`: Reverted the ability to use flow exports with variable values removed. Variable values are required when importing flows using this provider. +``` + +```release-note:bug +`resource/davinci_flow`: Resolve warnings that state that DaVinci JSON files contain unknown properties when using flow variable nodes. +``` + +```release-note:note +`resource/davinci_flow`: Enhanced error messages that result from invalid flow formats. +``` + +```release-note:note +Bump `github.com/samir-gandhi/davinci-client-go` from 0.4.0 => 0.5.0 +``` diff --git a/docs/resources/flow.md b/docs/resources/flow.md index 70673287..401fb69e 100644 --- a/docs/resources/flow.md +++ b/docs/resources/flow.md @@ -9,6 +9,8 @@ description: |- Resource to import and manage a DaVinci flow in an environment. Connection and Subflow references in the JSON export can be overridden with ones managed by Terraform, see the examples and schema below for details. +!> Only flows that include variable values are supported. Flows that have been exported from a source system with the "Include Variable Values" admin console tickbox unchecked will not be imported correctly. + !> When flow, flow instance or company variables are embedded in the `flow_json`, only the `context`, `type` and `name` of the variables are managed by this resource. To manage the mutability, value, description, minimum and maximum values of an imported variable, the `davinci_variable` resource must be used. ~> When using "company" or "flow instance" variables, it is recommended to define these variables using the `davinci_variable` resource before the flows that depend on them. This is shown in the example using the `depends_on` meta argument. diff --git a/go.mod b/go.mod index ded0a64c..d6d46e5b 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/patrickcping/pingone-go-sdk-v2 v0.12.3 github.com/patrickcping/pingone-go-sdk-v2/management v0.43.0 github.com/pavius/impi v0.0.3 - github.com/samir-gandhi/davinci-client-go v0.4.0 + github.com/samir-gandhi/davinci-client-go v0.5.0 github.com/samir-gandhi/dvgenerate v0.0.11 github.com/terraform-linters/tflint v0.53.0 ) diff --git a/go.sum b/go.sum index eda22533..6cbcf29c 100644 --- a/go.sum +++ b/go.sum @@ -929,8 +929,8 @@ github.com/ryancurrah/gomodguard v1.3.3 h1:eiSQdJVNr9KTNxY2Niij8UReSwR8Xrte3exBr github.com/ryancurrah/gomodguard v1.3.3/go.mod h1:rsKQjj4l3LXe8N344Ow7agAy5p9yjsWOtRzUMYmA0QY= github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= -github.com/samir-gandhi/davinci-client-go v0.4.0 h1:XsjCOxZhm9AvhVREUuqFP09dMjHwnsk4pDAx6uPzaUk= -github.com/samir-gandhi/davinci-client-go v0.4.0/go.mod h1:DmOPy/WsIc4e7RYOIeSGM6Qrlb1JsYuN45UaGnlDt9U= +github.com/samir-gandhi/davinci-client-go v0.5.0 h1:8aYXLsfdepnR5seT7suqI2WTQkhueX/8KzvevHy5LBA= +github.com/samir-gandhi/davinci-client-go v0.5.0/go.mod h1:DmOPy/WsIc4e7RYOIeSGM6Qrlb1JsYuN45UaGnlDt9U= github.com/samir-gandhi/dvgenerate v0.0.11 h1:VMvkeCdI7DyzrPuE3clPFPvR1gO9s6Z24O8Gyh0d5h8= github.com/samir-gandhi/dvgenerate v0.0.11/go.mod h1:PvrK6c+8SdRSWKmyXsagKeVqSGoo9P8oBbWaIzFD1mQ= github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc= diff --git a/internal/acctest/flows/full-basic-novariablevalues.json b/internal/acctest/flows/full-basic-novariablevalues.json index 10be3ddd..b8c76992 100644 --- a/internal/acctest/flows/full-basic-novariablevalues.json +++ b/internal/acctest/flows/full-basic-novariablevalues.json @@ -1,5 +1,5 @@ { - "companyId": "942b4724-d83d-418c-966c-ed7d352a985c", + "companyId": "2c6123ae-108f-4d11-bcc2-6c8f4dfa9fdb", "authTokenExpireIds": [], "connectorIds": [ "httpConnector", @@ -8,19 +8,19 @@ "flowConnector", "variablesConnector" ], - "createdDate": 1723638023970, - "currentVersion": 1, + "createdDate": 1706708769850, + "currentVersion": 8, "customerId": "db5f4450b2bd8a56ce076dec0c358a9a", - "deployedDate": 1723638024066, - "description": "Imported on Wed Aug 14 2024 12:20:23 GMT+0000 (Coordinated Universal Time)", + "deployedDate": 1706709739837, + "description": "", "flowStatus": "enabled", "isOutputSchemaSaved": false, "name": "full-basic", - "publishedVersion": 1, + "publishedVersion": 8, "timeouts": "null", - "updatedDate": 1723638024104, - "flowId": "8236d08b476a5cf7b981fa53f6971019", - "versionId": 1, + "updatedDate": 1706709739837, + "flowId": "c7062a8857740ee2185694bb855f8f21", + "versionId": 8, "graphData": { "elements": { "nodes": [ @@ -28,9 +28,9 @@ "data": { "id": "1u2m5vzr49", "nodeType": "CONNECTION", - "connectionId": "481e952e6b11db8360587b8711620786", + "connectionId": "867ed4363b2bc21c860085ad2baa817d", "connectorId": "httpConnector", - "name": "HTTP", + "name": "Http", "label": "Http", "status": "configured", "capabilityName": "customHtmlMessage", @@ -77,9 +77,9 @@ "data": { "id": "nx0o1b2cmw", "nodeType": "CONNECTION", - "connectionId": "548ea933f35b9787ae12ad130f78045b", + "connectionId": "de650ca45593b82c49064ead10b9fe17", "connectorId": "functionsConnector", - "name": "abcd123-functions", + "name": "Functions", "label": "Functions", "status": "configured", "capabilityName": "AEqualsB", @@ -137,9 +137,9 @@ "data": { "id": "ikt13crnhy", "nodeType": "CONNECTION", - "connectionId": "481e952e6b11db8360587b8711620786", + "connectionId": "867ed4363b2bc21c860085ad2baa817d", "connectorId": "httpConnector", - "name": "HTTP", + "name": "Http", "label": "Http", "status": "configured", "capabilityName": "createSuccessResponse", @@ -163,9 +163,9 @@ "data": { "id": "vsp1ewtr9m", "nodeType": "CONNECTION", - "connectionId": "fa497c1ceaea43c0886d8d360874a53d", + "connectionId": "53ab83a4a4ab919d9f2cb02d9e111ac8", "connectorId": "errorConnector", - "name": "abcd123-error", + "name": "Error Message", "label": "Error Message", "status": "configured", "capabilityName": "customErrorMessage", @@ -193,9 +193,9 @@ "data": { "id": "xb74p6rkd8", "nodeType": "CONNECTION", - "connectionId": "84e29d2409ba66c0caf53f9cad0a2049", + "connectionId": "2581eb287bb1d9bd29ae9886d675f89f", "connectorId": "flowConnector", - "name": "abcd123-flow", + "name": "Flow Connector", "label": "Flow Conductor", "status": "configured", "capabilityName": "startUiSubFlow", @@ -229,9 +229,9 @@ "data": { "id": "kq5ybvwvro", "nodeType": "CONNECTION", - "connectionId": "84e29d2409ba66c0caf53f9cad0a2049", + "connectionId": "2581eb287bb1d9bd29ae9886d675f89f", "connectorId": "flowConnector", - "name": "abcd123-flow", + "name": "Flow Connector", "label": "Flow Conductor", "status": "configured", "capabilityName": "startUiSubFlow", @@ -301,9 +301,9 @@ "data": { "id": "3zvjdgdljx", "nodeType": "CONNECTION", - "connectionId": "9f8f97e94ad87e184960633b424d80b6", + "connectionId": "06922a684039827499bdbdd97f49827b", "connectorId": "variablesConnector", - "name": "abcd123-variables", + "name": "Variables", "label": "Variables", "status": "configured", "capabilityName": "saveFlowValue", @@ -589,11 +589,12 @@ } }, "flowColor": "#E3F0FF", - "savedDate": 1723638023903, + "savedDate": 1706708769645, "variables": [ { "context": "flow", - "createdDate": 1723638023769, + "createdDate": 1706708735989, + "customerId": "db5f4450b2bd8a56ce076dec0c358a9a", "fields": { "type": "string", "displayName": "", @@ -601,16 +602,16 @@ "min": 0, "max": 2000 }, - "flowId": "8236d08b476a5cf7b981fa53f6971019", - "id": "bfaf14e8-0756-4e78-800e-90f558391368", + "flowId": "c7062a8857740ee2185694bb855f8f21", "type": "property", "visibility": "private", - "name": "fdgdfgfdg##SK##flow##SK##8236d08b476a5cf7b981fa53f6971019", - "companyId": "942b4724-d83d-418c-966c-ed7d352a985c" + "name": "fdgdfgfdg##SK##flow##SK##c7062a8857740ee2185694bb855f8f21", + "companyId": "2c6123ae-108f-4d11-bcc2-6c8f4dfa9fdb" }, { "context": "flow", - "createdDate": 1723638023768, + "createdDate": 1706708761083, + "customerId": "db5f4450b2bd8a56ce076dec0c358a9a", "fields": { "type": "number", "displayName": "test123", @@ -618,13 +619,12 @@ "min": 4, "max": 20 }, - "flowId": "8236d08b476a5cf7b981fa53f6971019", - "id": "0389e818-4dd2-4203-b82f-ee5948f93287", + "flowId": "c7062a8857740ee2185694bb855f8f21", "type": "property", "visibility": "private", - "name": "test123##SK##flow##SK##8236d08b476a5cf7b981fa53f6971019", - "companyId": "942b4724-d83d-418c-966c-ed7d352a985c" + "name": "test123##SK##flow##SK##c7062a8857740ee2185694bb855f8f21", + "companyId": "2c6123ae-108f-4d11-bcc2-6c8f4dfa9fdb" } ], "connections": [] -} \ No newline at end of file +} diff --git a/internal/acctest/flows/full-basic-vars-add-variable.json b/internal/acctest/flows/full-basic-vars-add-variable.json index f634c7ca..50575512 100644 --- a/internal/acctest/flows/full-basic-vars-add-variable.json +++ b/internal/acctest/flows/full-basic-vars-add-variable.json @@ -315,19 +315,22 @@ "name": "fdgdfgfdg", "key": 0.8936786494474329, "label": "fdgdfgfdg (string - flow)", - "type": "string" + "type": "string", + "value": "fdgdfgfdgValue" }, { "name": "fdgdfgfdgNEW", "key": 0.8936786494474330, "label": "fdgdfgfdgNEW (string - flow)", - "type": "string" + "type": "string", + "value": "fdgdfgfdgNEWValue" }, { "name": "test123", "key": 0.379286774724122, "label": "test123 (number - flow)", - "type": "number" + "type": "number", + "value": 5 } ] } @@ -382,7 +385,8 @@ "name": "flowInstanceVariable1", "key": 0.09068454768967449, "label": "flowInstanceVariable1 (string - flowInstance)", - "type": "string" + "type": "string", + "value": "flowInstanceVariable1Value" } ] } @@ -437,7 +441,8 @@ "name": "testuser", "key": 0.9814043007447408, "label": "testuser (string - flowInstance)", - "type": "string" + "type": "string", + "value": "testuserValue" } ] } diff --git a/internal/acctest/flows/full-basic-vars-modify-variable.json b/internal/acctest/flows/full-basic-vars-modify-variable.json index 7b87e539..29c7b72a 100644 --- a/internal/acctest/flows/full-basic-vars-modify-variable.json +++ b/internal/acctest/flows/full-basic-vars-modify-variable.json @@ -315,13 +315,15 @@ "name": "fdgdfgfdg", "key": 0.8936786494474329, "label": "fdgdfgfdg (number - flow)", - "type": "number" + "type": "number", + "value": 10 }, { "name": "test123", "key": 0.379286774724122, "label": "test123 (number - flow)", - "type": "number" + "type": "number", + "value": 5 } ] } @@ -376,7 +378,8 @@ "name": "flowInstanceVariable1", "key": 0.09068454768967449, "label": "flowInstanceVariable1 (string - flowInstance)", - "type": "string" + "type": "string", + "value": "flowInstanceVariable1Value" } ] } @@ -431,7 +434,8 @@ "name": "testuser", "key": 0.9814043007447408, "label": "testuser (string - flowInstance)", - "type": "string" + "type": "string", + "value": "testuserValue" } ] } diff --git a/internal/acctest/flows/full-basic-vars-remove-variable.json b/internal/acctest/flows/full-basic-vars-remove-variable.json index 4ef27a47..7d3cd24b 100644 --- a/internal/acctest/flows/full-basic-vars-remove-variable.json +++ b/internal/acctest/flows/full-basic-vars-remove-variable.json @@ -315,7 +315,8 @@ "name": "test123", "key": 0.379286774724122, "label": "test123 (number - flow)", - "type": "number" + "type": "number", + "value": 5 } ] } @@ -370,7 +371,8 @@ "name": "flowInstanceVariable1", "key": 0.09068454768967449, "label": "flowInstanceVariable1 (string - flowInstance)", - "type": "string" + "type": "string", + "value": "flowInstanceVariable1Value" } ] } @@ -425,7 +427,8 @@ "name": "testuser", "key": 0.9814043007447408, "label": "testuser (string - flowInstance)", - "type": "string" + "type": "string", + "value": "testuserValue" } ] } diff --git a/internal/acctest/flows/full-basic-vars.json b/internal/acctest/flows/full-basic-vars.json index 9250854d..91ba3b21 100644 --- a/internal/acctest/flows/full-basic-vars.json +++ b/internal/acctest/flows/full-basic-vars.json @@ -315,13 +315,15 @@ "name": "fdgdfgfdg", "key": 0.8936786494474329, "label": "fdgdfgfdg (string - flow)", - "type": "string" + "type": "string", + "value": "fdgdfgfdgValue" }, { "name": "test123", "key": 0.379286774724122, "label": "test123 (number - flow)", - "type": "number" + "type": "number", + "value": "5" } ] } @@ -376,7 +378,8 @@ "name": "flowInstanceVariable1", "key": 0.09068454768967449, "label": "flowInstanceVariable1 (string - flowInstance)", - "type": "string" + "type": "string", + "value": "flowInstanceVariable1Value" } ] } @@ -431,7 +434,8 @@ "name": "testuser", "key": 0.9814043007447408, "label": "testuser (string - flowInstance)", - "type": "string" + "type": "string", + "value": "testuserValue" } ] } diff --git a/internal/framework/customtypes/davinciexporttype/parsed_value.go b/internal/framework/customtypes/davinciexporttype/parsed_value.go index 1a07b7f3..e5e778da 100644 --- a/internal/framework/customtypes/davinciexporttype/parsed_value.go +++ b/internal/framework/customtypes/davinciexporttype/parsed_value.go @@ -3,6 +3,7 @@ package davinciexporttype import ( "context" "encoding/json" + "errors" "fmt" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -88,6 +89,7 @@ func (v ParsedValue) StringSemanticEquals(ctx context.Context, newValuable baset IgnoreVersionMetadata: v.IgnoreVersionMetadata, IgnoreFlowMetadata: v.IgnoreFlowMetadata, IgnoreFlowVariables: v.IgnoreFlowVariables, + NodeOpts: v.NodeOpts, }), diags } @@ -97,17 +99,8 @@ func (v ParsedValue) ValidateAttribute(ctx context.Context, req xattr.ValidateAt return } - ok, _, _, err := davinci.ValidFlowsInfoExport([]byte(v.ValueString()), davinci.ExportCmpOpts{}) - if err != nil { - resp.Diagnostics.AddError( - "DaVinci Export JSON String Validation Error", - "An unexpected error was encountered while validating the DaVinci Export JSON string. This is always an error in the provider. "+ - "Please report the following to the provider developer:\n\n"+err.Error(), - ) - return - } - - if ok { + err := davinci.ValidFlowsInfoExport([]byte(v.ValueString()), davinci.ExportCmpOpts{}) + if err == nil { resp.Diagnostics.AddAttributeError( req.Path, "Invalid DaVinci Flow Export String Value", @@ -118,8 +111,22 @@ func (v ParsedValue) ValidateAttribute(ctx context.Context, req xattr.ValidateAt return } + var equatesEmptyError *davinci.EquatesEmptyTypeError + switch { + case errors.Is(err, davinci.ErrEmptyFlow), errors.As(err, &equatesEmptyError): // Isn't a multi-flow export, this is good so we break here + break + default: + resp.Diagnostics.AddAttributeError( + req.Path, + "Invalid DaVinci Flow Export String Value", + "A string value was provided that is not valid DaVinci Export JSON for this provider.\n\n"+ + v.parseValidationErrorMessage(err) + "\n", + ) + return + } + // Validate just the config of the export - ok, errorCode, _, err := davinci.ValidFlowExport([]byte(v.ValueString()), davinci.ExportCmpOpts{ + err = davinci.ValidFlowExport([]byte(v.ValueString()), davinci.ExportCmpOpts{ IgnoreConfig: v.IgnoreConfig, IgnoreDesignerCues: v.IgnoreDesignerCues, IgnoreEnvironmentMetadata: v.IgnoreEnvironmentMetadata, @@ -127,26 +134,28 @@ func (v ParsedValue) ValidateAttribute(ctx context.Context, req xattr.ValidateAt IgnoreVersionMetadata: v.IgnoreVersionMetadata, IgnoreFlowMetadata: v.IgnoreFlowMetadata, IgnoreFlowVariables: v.IgnoreFlowVariables, + NodeOpts: v.NodeOpts, }) - - if !ok { + + if err != nil { tflog.Debug(ctx, "Invalid DaVinci Flow Export String Value", map[string]interface{}{ - "error code": string(errorCode), - //"diff": &diff, - we don't want to expose this in the logs as it may contain sensitive information - "error": err, + "error": err, }) + + + resp.Diagnostics.AddAttributeError( req.Path, "Invalid DaVinci Flow Export String Value", - "A string value was provided that is not valid DaVinci Export JSON string format.\n\n"+ - "Please re-export the DaVinci flow. If the flow JSON has been correctly exported from the DaVinci environment (and can be re-imported), please report this error to the provider maintainers.\n", + "A string value was provided that is not valid DaVinci Export JSON for this provider.\n\n"+ + v.parseValidationErrorMessage(err) + "\n", ) return } // Warn in case there are AdditionalProperties in the import file (since these aren't cleanly handled in the SDK, while they are preserved on import, there may be unpredictable results in diff calculation) - ok, errorCode, _, err = davinci.ValidFlowExport([]byte(v.ValueString()), davinci.ExportCmpOpts{ + err = davinci.ValidFlowExport([]byte(v.ValueString()), davinci.ExportCmpOpts{ IgnoreConfig: true, IgnoreDesignerCues: true, IgnoreEnvironmentMetadata: true, @@ -154,19 +163,18 @@ func (v ParsedValue) ValidateAttribute(ctx context.Context, req xattr.ValidateAt IgnoreVersionMetadata: true, IgnoreFlowMetadata: true, IgnoreFlowVariables: true, + NodeOpts: v.NodeOpts, }) - - if !v.IgnoreUnmappedProperties && !ok { + + if !v.IgnoreUnmappedProperties && err != nil { tflog.Debug(ctx, "Invalid DaVinci Flow Export String Value", map[string]interface{}{ - "error code": string(errorCode), - //"diff": &diff, - we don't want to expose this in the logs as it may contain sensitive information - "error": err, + "error": err, }) resp.Diagnostics.AddAttributeWarning( req.Path, "DaVinci Export JSON contains unknown properties", - "The DaVinci Flow Export contains properties that cannot be validated. These parameters will be preserved on import to the DaVinci service, but there may be unpredictable results in difference calculation.\n", + v.parseValidationErrorMessage(err) + "\n", ) } } @@ -196,3 +204,33 @@ func NewParsedPointerValue(value *string, cmpOpts davinci.ExportCmpOpts) ParsedV ExportCmpOpts: cmpOpts, } } + +func (v ParsedValue) parseValidationErrorMessage(err error) string { + var equatesEmptyError *davinci.EquatesEmptyTypeError + var missingRequiredFlowFieldsError *davinci.MissingRequiredFlowFieldsTypeError + var unknownAdditionalFieldsError *davinci.UnknownAdditionalFieldsTypeError + var minFlowDefsError *davinci.MinFlowDefinitionsExceededTypeError + var maxFlowDefsError *davinci.MaxFlowDefinitionsExceededTypeError + switch { + case errors.Is(err, davinci.ErrInvalidJson): + return "The DaVinci Flow Export JSON is not valid JSON. Please re-export the DaVinci flow." + case errors.Is(err, davinci.ErrEmptyFlow): + return "The DaVinci Flow Export JSON is empty. Please re-export the DaVinci flow." + case errors.Is(err, davinci.ErrNoFlowDefinition): + return "No flow definition found in the DaVinci Flow Export JSON. Expecting exactly one flow definition. Please re-export the DaVinci flow." + case errors.Is(err, davinci.ErrMissingSaveVariableValues): + return "Save flow variable nodes are present but are missing variable values in the DaVinci Flow Export JSON. Please re-export the DaVinci flow ensuring that variable values are included." + case errors.As(err, &equatesEmptyError): + return "The DaVinci Flow Export JSON has been evaluated to be empty according to plan diff criteria. Please re-export the DaVinci flow." + case errors.As(err, &missingRequiredFlowFieldsError): + return "The DaVinci Flow Export JSON has been evaluated to be missing required fields. Please re-export the DaVinci flow." + case errors.As(err, &unknownAdditionalFieldsError): + return "The DaVinci Flow Export contains unknown properties that cannot be validated. These parameters will be preserved on import to the DaVinci service, but there may be unpredictable results in difference calculation." + case errors.As(err, &minFlowDefsError): + return fmt.Sprintf("There are not enough flows exported in the flow group. Expecting a minimum of %d", minFlowDefsError.Min) + case errors.As(err, &maxFlowDefsError): + return fmt.Sprintf("There are too many flows exported in the flow group. Expecting a maximum of %d", maxFlowDefsError.Max) + default: + return fmt.Sprintf("An unexpected error was encountered while validating the DaVinci Export JSON string: %s. This is always an error in the provider and should be reported to the provider maintainers. ", err) + } +} \ No newline at end of file diff --git a/internal/service/davinci/resource_flow.go b/internal/service/davinci/resource_flow.go index b5fad988..d87cc35e 100644 --- a/internal/service/davinci/resource_flow.go +++ b/internal/service/davinci/resource_flow.go @@ -82,6 +82,11 @@ var ( IgnoreVersionMetadata: true, IgnoreFlowMetadata: false, IgnoreFlowVariables: false, + NodeOpts: &davinci.ExportNodeCmpOpts{ + VariablesConnector: &davinci.ExportNodeVariablesCmpOpts{ + ExpectVariableValues: true, // The input field needs validation + }, + }, } flowConfigurationJsonCmpOptsConfiguration = davinci.ExportCmpOpts{ @@ -92,6 +97,11 @@ var ( IgnoreVersionMetadata: true, IgnoreFlowMetadata: true, IgnoreFlowVariables: true, + NodeOpts: &davinci.ExportNodeCmpOpts{ + VariablesConnector: &davinci.ExportNodeVariablesCmpOpts{ + ExpectVariableValues: false, // We don't need to validate this + }, + }, } flowExportJsonCmpOptsConfiguration = davinci.ExportCmpOpts{ @@ -102,6 +112,11 @@ var ( IgnoreVersionMetadata: false, IgnoreFlowMetadata: false, IgnoreFlowVariables: true, // because this is handled by another resource + NodeOpts: &davinci.ExportNodeCmpOpts{ + VariablesConnector: &davinci.ExportNodeVariablesCmpOpts{ + ExpectVariableValues: false, // We don't need to validate this + }, + }, } ) @@ -642,7 +657,7 @@ func (r *FlowResource) Create(ctx context.Context, req resource.CreateRequest, r r.Client, environmentID, func() (interface{}, *http.Response, error) { - return r.Client.ReadFlowVersionOptionalVariableWithResponse(environmentID, createFlow.FlowID, nil, false) + return r.Client.ReadFlowVersionOptionalVariableWithResponse(environmentID, createFlow.FlowID, nil, true) }, ) if err != nil { @@ -695,7 +710,7 @@ func (r *FlowResource) Read(ctx context.Context, req resource.ReadRequest, resp r.Client, environmentID, func() (interface{}, *http.Response, error) { - return r.Client.ReadFlowVersionOptionalVariableWithResponse(environmentID, flowID, nil, false) + return r.Client.ReadFlowVersionOptionalVariableWithResponse(environmentID, flowID, nil, true) }, ) @@ -878,7 +893,7 @@ func (r *FlowResource) Update(ctx context.Context, req resource.UpdateRequest, r r.Client, environmentID, func() (interface{}, *http.Response, error) { - return r.Client.ReadFlowVersionOptionalVariableWithResponse(environmentID, flowID, nil, false) + return r.Client.ReadFlowVersionOptionalVariableWithResponse(environmentID, flowID, nil, true) }, ) if err != nil { diff --git a/internal/service/davinci/resource_flow_test.go b/internal/service/davinci/resource_flow_test.go index f7ae13e1..9157e28b 100644 --- a/internal/service/davinci/resource_flow_test.go +++ b/internal/service/davinci/resource_flow_test.go @@ -1014,6 +1014,37 @@ func testAccResourceFlow_Variables(t *testing.T, withBootstrapConfig bool) { }) } + + +func TestAccResourceFlow_Variables_Invalid(t *testing.T) { + + resourceName := acctest.ResourceNameGen() + + name := resourceName + + fullStepHcl, _, err := testAccResourceFlow_Full_VariablesRedacted(resourceName, name, false) + if err != nil { + t.Fatalf("Failed to get HCL: %v", err) + } + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheckClient(t) + acctest.PreCheckNewEnvironment(t) + }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + ExternalProviders: acctest.ExternalProviders, + ErrorCheck: acctest.ErrorCheck(t), + CheckDestroy: davinci.Flow_CheckDestroy(), + Steps: []resource.TestStep{ + { + Config: fullStepHcl, + ExpectError: regexp.MustCompile(`Invalid DaVinci Flow Export String Value`), + }, + }, + }) +} + func TestAccResourceFlow_Variables_Overridden_Clean(t *testing.T) { testAccResourceFlow_Variables_Overridden(t, false) } @@ -1382,6 +1413,84 @@ EOT }`, acctest.PingoneEnvironmentSsoHcl(resourceName, withBootstrapConfig), commonHcl, resourceName, mainFlowJson), mainFlowJson, nil } +func testAccResourceFlow_Full_VariablesRedacted(resourceName, name string, withBootstrapConfig bool) (hcl, mainFlowJson string, err error) { + + mainFlowJson, err = acctest.ReadFlowJsonFile("flows/full-basic-novariablevalues.json") + if err != nil { + return "", "", err + } + + commonHcl, err := testAccResourceFlow_Common_WithMappingIDs_HCL(resourceName, name) + if err != nil { + return "", "", err + } + + return fmt.Sprintf(` +%[1]s + +%[2]s + +resource "davinci_flow" "%[3]s" { + environment_id = pingone_environment.%[3]s.id + + name = "my awesome flow" + description = "my awesome flow description" + + flow_json = < Only flows that include variable values are supported. Flows that have been exported from a source system with the "Include Variable Values" admin console tickbox unchecked will not be imported correctly. + !> When flow, flow instance or company variables are embedded in the `flow_json`, only the `context`, `type` and `name` of the variables are managed by this resource. To manage the mutability, value, description, minimum and maximum values of an imported variable, the `davinci_variable` resource must be used. ~> When using "company" or "flow instance" variables, it is recommended to define these variables using the `davinci_variable` resource before the flows that depend on them. This is shown in the example using the `depends_on` meta argument.