From 51ab562aae83c3455664a7de7af9d6e653f465a6 Mon Sep 17 00:00:00 2001 From: Patrick Cowland <44225864+patrickcping@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:46:21 +0100 Subject: [PATCH] `davinci_flow`: Fix multi-flow validation (#367) * `davinci_flow`: Fix multi-flow validation * changelog --- .changelog/367.txt | 3 + internal/acctest/flows/multiple-flows.json | 952 ++++++++++++++++++ internal/acctest/testdata.go | 14 + .../davinciexporttype/parsed_value.go | 71 +- .../service/davinci/resource_flow_test.go | 47 +- 5 files changed, 1047 insertions(+), 40 deletions(-) create mode 100644 .changelog/367.txt create mode 100644 internal/acctest/flows/multiple-flows.json diff --git a/.changelog/367.txt b/.changelog/367.txt new file mode 100644 index 00000000..7e42d182 --- /dev/null +++ b/.changelog/367.txt @@ -0,0 +1,3 @@ +```release-note:bug +`resource/davinci_flow`: Fix validation to test whether the flow JSON contains multiple flows in one file. Only single flows are supported. +``` \ No newline at end of file diff --git a/internal/acctest/flows/multiple-flows.json b/internal/acctest/flows/multiple-flows.json new file mode 100644 index 00000000..3065617b --- /dev/null +++ b/internal/acctest/flows/multiple-flows.json @@ -0,0 +1,952 @@ +{ + "flows": [ + { + "companyId": "942b4724-d83d-418c-966c-ed7d352a985c", + "authTokenExpireIds": [], + "connectorIds": [ + "httpConnector", + "functionsConnector", + "errorConnector", + "flowConnector", + "variablesConnector" + ], + "createdDate": 1724341117612, + "currentVersion": 3, + "customerId": "db5f4450b2bd8a56ce076dec0c358a9a", + "description": "Imported on Wed Aug 14 2024 12:20:23 GMT+0000 (Coordinated Universal Time)", + "flowStatus": "enabled", + "isOutputSchemaSaved": false, + "name": "full-basic", + "publishedVersion": 3, + "timeouts": "null", + "flowId": "8236d08b476a5cf7b981fa53f6971019", + "versionId": 3, + "graphData": { + "elements": { + "nodes": [ + { + "data": { + "id": "1u2m5vzr49", + "nodeType": "CONNECTION", + "connectionId": "481e952e6b11db8360587b8711620786", + "connectorId": "httpConnector", + "name": "HTTP", + "label": "Http", + "status": "configured", + "capabilityName": "customHtmlMessage", + "type": "trigger", + "properties": { + "message": { + "value": "[\n {\n \"children\": [\n {\n \"text\": \"Hello, world?\"\n }\n ]\n }\n]" + } + } + }, + "position": { + "x": 284, + "y": 392 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + }, + { + "data": { + "id": "8fvg7tfr8j", + "nodeType": "EVAL", + "label": "Evaluator" + }, + "position": { + "x": 433.5, + "y": 393.25 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + }, + { + "data": { + "id": "nx0o1b2cmw", + "nodeType": "CONNECTION", + "connectionId": "548ea933f35b9787ae12ad130f78045b", + "connectorId": "functionsConnector", + "name": "abcd123-functions", + "label": "Functions", + "status": "configured", + "capabilityName": "AEqualsB", + "type": "trigger", + "properties": { + "leftValueA": { + "value": "[\n {\n \"children\": [\n {\n \"text\": \"1\"\n }\n ]\n }\n]" + }, + "rightValueB": { + "value": "[\n {\n \"children\": [\n {\n \"text\": \"1\"\n }\n ]\n }\n]" + } + } + }, + "position": { + "x": 583, + "y": 394.5 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + }, + { + "data": { + "id": "cdcw8k7dnx", + "nodeType": "EVAL", + "label": "Evaluator", + "properties": { + "vsp1ewtr9m": { + "value": "allTriggersFalse" + }, + "xb74p6rkd8": { + "value": "anyTriggersFalse" + } + } + }, + "position": { + "x": 724, + "y": 382 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + }, + { + "data": { + "id": "ikt13crnhy", + "nodeType": "CONNECTION", + "connectionId": "481e952e6b11db8360587b8711620786", + "connectorId": "httpConnector", + "name": "HTTP", + "label": "Http", + "status": "configured", + "capabilityName": "createSuccessResponse", + "type": "action", + "properties": {} + }, + "position": { + "x": 1204, + "y": 322 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + }, + { + "data": { + "id": "vsp1ewtr9m", + "nodeType": "CONNECTION", + "connectionId": "fa497c1ceaea43c0886d8d360874a53d", + "connectorId": "errorConnector", + "name": "abcd123-error", + "label": "Error Message", + "status": "configured", + "capabilityName": "customErrorMessage", + "type": "action", + "properties": { + "errorMessage": { + "value": "[\n {\n \"children\": [\n {\n \"text\": \"Error\"\n }\n ]\n }\n]" + } + } + }, + "position": { + "x": 1204, + "y": 472 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + }, + { + "data": { + "id": "xb74p6rkd8", + "nodeType": "CONNECTION", + "connectionId": "84e29d2409ba66c0caf53f9cad0a2049", + "connectorId": "flowConnector", + "name": "abcd123-flow", + "label": "Flow Conductor", + "status": "configured", + "capabilityName": "startUiSubFlow", + "type": "trigger", + "properties": { + "subFlowId": { + "value": { + "label": "abcd123-subflow-2", + "value": "29aea97a66e792a630b96597faf337cd" + } + }, + "subFlowVersionId": { + "value": -1 + } + } + }, + "position": { + "x": 874, + "y": 502 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + }, + { + "data": { + "id": "kq5ybvwvro", + "nodeType": "CONNECTION", + "connectionId": "84e29d2409ba66c0caf53f9cad0a2049", + "connectorId": "flowConnector", + "name": "abcd123-flow", + "label": "Flow Conductor", + "status": "configured", + "capabilityName": "startUiSubFlow", + "type": "trigger", + "properties": { + "subFlowId": { + "value": { + "label": "abcd123-subflow-1", + "value": "e7cf51f064dad2d3888f55c4616e5c37" + } + }, + "subFlowVersionId": { + "value": -1 + } + } + }, + "position": { + "x": 874, + "y": 292 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + }, + { + "data": { + "id": "j74pmg6577", + "nodeType": "EVAL" + }, + "position": { + "x": 1024, + "y": 292 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + }, + { + "data": { + "id": "pensvkew7y", + "nodeType": "EVAL" + }, + "position": { + "x": 1039, + "y": 487 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + }, + { + "data": { + "id": "3zvjdgdljx", + "nodeType": "CONNECTION", + "connectionId": "9f8f97e94ad87e184960633b424d80b6", + "connectorId": "variablesConnector", + "name": "abcd123-variables", + "label": "Variables", + "status": "configured", + "capabilityName": "saveFlowValue", + "type": "trigger", + "properties": { + "saveFlowVariables": { + "value": [ + { + "name": "fdgdfgfdg", + "value": "[\n {\n \"children\": [\n {\n \"text\": \"test124\"\n }\n ]\n }\n]", + "key": 0.8936786494474329, + "label": "fdgdfgfdg (string - flow)", + "type": "string" + }, + { + "name": "test123", + "value": "[\n {\n \"children\": [\n {\n \"text\": \"test456\"\n }\n ]\n }\n]", + "key": 0.379286774724122, + "label": "test123 (number - flow)", + "type": "number" + } + ] + } + } + }, + "position": { + "x": 277, + "y": 236 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + }, + { + "data": { + "id": "bbemfztdyk", + "nodeType": "EVAL" + }, + "position": { + "x": 280.5, + "y": 314 + }, + "group": "nodes", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": false, + "classes": "" + } + ], + "edges": [ + { + "data": { + "id": "hseww5vtf0", + "source": "1u2m5vzr49", + "target": "8fvg7tfr8j" + }, + "position": { + "x": 0, + "y": 0 + }, + "group": "edges", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": true, + "classes": "" + }, + { + "data": { + "id": "ljavni2nky", + "source": "8fvg7tfr8j", + "target": "nx0o1b2cmw" + }, + "position": { + "x": 0, + "y": 0 + }, + "group": "edges", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": true, + "classes": "" + }, + { + "data": { + "id": "0o2fqy3mf3", + "source": "nx0o1b2cmw", + "target": "cdcw8k7dnx" + }, + "position": { + "x": 0, + "y": 0 + }, + "group": "edges", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": true, + "classes": "" + }, + { + "data": { + "id": "493yd0jbi6", + "source": "cdcw8k7dnx", + "target": "kq5ybvwvro" + }, + "position": { + "x": 0, + "y": 0 + }, + "group": "edges", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": true, + "classes": "" + }, + { + "data": { + "id": "pn2kixnzms", + "source": "j74pmg6577", + "target": "ikt13crnhy" + }, + "position": { + "x": 0, + "y": 0 + }, + "group": "edges", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": true, + "classes": "" + }, + { + "data": { + "id": "0sb4quzlgx", + "source": "kq5ybvwvro", + "target": "j74pmg6577" + }, + "position": { + "x": 0, + "y": 0 + }, + "group": "edges", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": true, + "classes": "" + }, + { + "data": { + "id": "v5p4i55lt9", + "source": "cdcw8k7dnx", + "target": "xb74p6rkd8" + }, + "position": { + "x": 0, + "y": 0 + }, + "group": "edges", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": true, + "classes": "" + }, + { + "data": { + "id": "k0trrhjqt6", + "source": "xb74p6rkd8", + "target": "pensvkew7y" + }, + "position": { + "x": 0, + "y": 0 + }, + "group": "edges", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": true, + "classes": "" + }, + { + "data": { + "id": "2g0chago4l", + "source": "pensvkew7y", + "target": "vsp1ewtr9m" + }, + "position": { + "x": 0, + "y": 0 + }, + "group": "edges", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": true, + "classes": "" + }, + { + "data": { + "id": "gs1fx4x303", + "source": "3zvjdgdljx", + "target": "bbemfztdyk" + }, + "position": { + "x": 0, + "y": 0 + }, + "group": "edges", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": true, + "classes": "" + }, + { + "data": { + "id": "cum544luro", + "source": "bbemfztdyk", + "target": "1u2m5vzr49" + }, + "position": { + "x": 0, + "y": 0 + }, + "group": "edges", + "removed": false, + "selected": false, + "selectable": true, + "locked": false, + "grabbable": true, + "pannable": true, + "classes": "" + } + ] + }, + "data": {}, + "zoomingEnabled": true, + "userZoomingEnabled": true, + "zoom": 1, + "minZoom": 1e-50, + "maxZoom": 1e+50, + "panningEnabled": true, + "userPanningEnabled": true, + "pan": { + "x": 0, + "y": 0 + }, + "boxSelectionEnabled": true, + "renderer": { + "name": "null" + } + }, + "flowColor": "#E3F0FF", + "savedDate": 1724341117551, + "variables": [ + { + "context": "flow", + "createdDate": 1723638023769, + "fields": { + "type": "string", + "displayName": "", + "mutable": true, + "min": 0, + "max": 2000 + }, + "flowId": "8236d08b476a5cf7b981fa53f6971019", + "id": "bfaf14e8-0756-4e78-800e-90f558391368", + "type": "property", + "visibility": "private", + "name": "fdgdfgfdg##SK##flow##SK##8236d08b476a5cf7b981fa53f6971019", + "companyId": "942b4724-d83d-418c-966c-ed7d352a985c" + }, + { + "context": "flow", + "createdDate": 1723638023768, + "fields": { + "type": "number", + "displayName": "test123", + "value": "10", + "mutable": true, + "min": 4, + "max": 20 + }, + "flowId": "8236d08b476a5cf7b981fa53f6971019", + "id": "0389e818-4dd2-4203-b82f-ee5948f93287", + "type": "property", + "visibility": "private", + "name": "test123##SK##flow##SK##8236d08b476a5cf7b981fa53f6971019", + "companyId": "942b4724-d83d-418c-966c-ed7d352a985c" + } + ], + "connections": [], + "parentFlowId": "8236d08b476a5cf7b981fa53f6971019" + }, + { + "companyId": "942b4724-d83d-418c-966c-ed7d352a985c", + "authTokenExpireIds": [], + "connectorIds": [ + "httpConnector" + ], + "createdDate": 1716478636308, + "currentVersion": 2, + "customerId": "268386b91d52f15318766610e740d0b1", + "description": "Cloned on Wed Jan 31 2024 13:43:43 GMT+0000 (Coordinated Universal Time). \n", + "flowStatus": "enabled", + "isOutputSchemaSaved": false, + "name": "abcd123-subflow-2", + "publishedVersion": 2, + "timeouts": "null", + "updatedDate": 1716478636766, + "flowId": "29aea97a66e792a630b96597faf337cd", + "versionId": 2, + "graphData": { + "boxSelectionEnabled": true, + "elements": { + "edges": [ + { + "classes": "", + "data": { + "id": "jv7enynltp", + "source": "9awrr4q360", + "target": "rbi38g672i" + }, + "grabbable": true, + "group": "edges", + "locked": false, + "pannable": true, + "position": { + "x": 0, + "y": 0 + }, + "removed": false, + "selectable": true, + "selected": false + }, + { + "classes": "", + "data": { + "id": "bn6hy8ycra", + "source": "rbi38g672i", + "target": "exljnczoqz" + }, + "grabbable": true, + "group": "edges", + "locked": false, + "pannable": true, + "position": { + "x": 0, + "y": 0 + }, + "removed": false, + "selectable": true, + "selected": false + } + ], + "nodes": [ + { + "classes": "", + "data": { + "capabilityName": "customHtmlMessage", + "connectionId": "9cb5e3fdbbf0eeb602e0ff332ad79e5d", + "connectorId": "httpConnector", + "id": "9awrr4q360", + "label": "Http", + "name": "abcd123-http", + "nodeType": "CONNECTION", + "properties": { + "message": { + "value": "[\n {\n \"children\": [\n {\n \"text\": \"Subflow 2a\"\n }\n ]\n }\n]" + } + }, + "status": "configured", + "type": "trigger" + }, + "grabbable": true, + "group": "nodes", + "locked": false, + "pannable": false, + "position": { + "x": 277, + "y": 236 + }, + "removed": false, + "selectable": true, + "selected": false + }, + { + "classes": "", + "data": { + "id": "rbi38g672i", + "label": "Evaluator", + "nodeType": "EVAL" + }, + "grabbable": true, + "group": "nodes", + "locked": false, + "pannable": false, + "position": { + "x": 394, + "y": 237.25 + }, + "removed": false, + "selectable": true, + "selected": false + }, + { + "classes": "", + "data": { + "capabilityName": "createSuccessResponse", + "connectionId": "9cb5e3fdbbf0eeb602e0ff332ad79e5d", + "connectorId": "httpConnector", + "id": "exljnczoqz", + "label": "HTTP", + "name": "abcd123-http", + "nodeType": "CONNECTION", + "status": "configured", + "type": "action" + }, + "grabbable": true, + "group": "nodes", + "locked": false, + "pannable": false, + "position": { + "x": 511, + "y": 238.5 + }, + "removed": false, + "selectable": true, + "selected": false + } + ] + }, + "maxZoom": 1e+50, + "minZoom": 1e-50, + "pan": { + "x": 0, + "y": 0 + }, + "panningEnabled": true, + "renderer": { + "name": "null" + }, + "userPanningEnabled": true, + "userZoomingEnabled": true, + "zoom": 1, + "zoomingEnabled": true + }, + "flowColor": "#AFD5FF", + "savedDate": 1716478636288, + "variables": [] + }, + { + "companyId": "942b4724-d83d-418c-966c-ed7d352a985c", + "authTokenExpireIds": [], + "connectorIds": [ + "httpConnector" + ], + "createdDate": 1716478636115, + "currentVersion": 2, + "customerId": "268386b91d52f15318766610e740d0b1", + "description": "Imported on Thu May 23 2024 15:37:14 GMT+0000 (Coordinated Universal Time)", + "flowStatus": "enabled", + "isOutputSchemaSaved": false, + "name": "abcd123-subflow-1", + "publishedVersion": 2, + "timeouts": "null", + "updatedDate": 1716478636560, + "flowId": "e7cf51f064dad2d3888f55c4616e5c37", + "versionId": 2, + "graphData": { + "boxSelectionEnabled": true, + "elements": { + "edges": [ + { + "classes": "", + "data": { + "id": "jv7enynltp", + "source": "9awrr4q360", + "target": "rbi38g672i" + }, + "grabbable": true, + "group": "edges", + "locked": false, + "pannable": true, + "position": { + "x": 0, + "y": 0 + }, + "removed": false, + "selectable": true, + "selected": false + }, + { + "classes": "", + "data": { + "id": "bn6hy8ycra", + "source": "rbi38g672i", + "target": "exljnczoqz" + }, + "grabbable": true, + "group": "edges", + "locked": false, + "pannable": true, + "position": { + "x": 0, + "y": 0 + }, + "removed": false, + "selectable": true, + "selected": false + } + ], + "nodes": [ + { + "classes": "", + "data": { + "capabilityName": "customHtmlMessage", + "connectionId": "9cb5e3fdbbf0eeb602e0ff332ad79e5d", + "connectorId": "httpConnector", + "id": "9awrr4q360", + "label": "Http", + "name": "abcd123-http", + "nodeType": "CONNECTION", + "properties": { + "message": { + "value": "[\n {\n \"children\": [\n {\n \"text\": \"Subflow 1\"\n }\n ]\n }\n]" + } + }, + "status": "configured", + "type": "trigger" + }, + "grabbable": true, + "group": "nodes", + "locked": false, + "pannable": false, + "position": { + "x": 277, + "y": 236 + }, + "removed": false, + "selectable": true, + "selected": false + }, + { + "classes": "", + "data": { + "id": "rbi38g672i", + "label": "Evaluator", + "nodeType": "EVAL" + }, + "grabbable": true, + "group": "nodes", + "locked": false, + "pannable": false, + "position": { + "x": 394, + "y": 237.25 + }, + "removed": false, + "selectable": true, + "selected": false + }, + { + "classes": "", + "data": { + "capabilityName": "createSuccessResponse", + "connectionId": "9cb5e3fdbbf0eeb602e0ff332ad79e5d", + "connectorId": "httpConnector", + "id": "exljnczoqz", + "label": "HTTP", + "name": "abcd123-http", + "nodeType": "CONNECTION", + "status": "configured", + "type": "action" + }, + "grabbable": true, + "group": "nodes", + "locked": false, + "pannable": false, + "position": { + "x": 511, + "y": 238.5 + }, + "removed": false, + "selectable": true, + "selected": false + } + ] + }, + "maxZoom": 1e+50, + "minZoom": 1e-50, + "pan": { + "x": 0, + "y": 0 + }, + "panningEnabled": true, + "renderer": { + "name": "null" + }, + "userPanningEnabled": true, + "userZoomingEnabled": true, + "zoom": 1, + "zoomingEnabled": true + }, + "flowColor": "#AFD5FF", + "savedDate": 1716478636091, + "variables": [] + } + ], + "companyId": "942b4724-d83d-418c-966c-ed7d352a985c", + "customerId": "db5f4450b2bd8a56ce076dec0c358a9a" +} \ No newline at end of file diff --git a/internal/acctest/testdata.go b/internal/acctest/testdata.go index 9c7453a2..fd8f3640 100644 --- a/internal/acctest/testdata.go +++ b/internal/acctest/testdata.go @@ -49,3 +49,17 @@ func ReadFlowJsonFile(path string) (string, error) { return string(mainFlowJsonBytes), nil } + +// takes path to json file, adjusts flow name attribute to include unique resource name, and returns json string +func ReadFlowJsonFileRaw(path string) (string, error) { + _, currentPath, _, ok := runtime.Caller(0) + if !ok { + return "", fmt.Errorf("failed to get current path") + } + flowByte, err := os.ReadFile(filepath.Join(filepath.Dir(currentPath), filepath.Clean(path))) + if err != nil { + return "", err + } + + return string(flowByte), nil +} diff --git a/internal/framework/customtypes/davinciexporttype/parsed_value.go b/internal/framework/customtypes/davinciexporttype/parsed_value.go index 49517187..4060cdd1 100644 --- a/internal/framework/customtypes/davinciexporttype/parsed_value.go +++ b/internal/framework/customtypes/davinciexporttype/parsed_value.go @@ -99,29 +99,28 @@ func (v ParsedValue) ValidateAttribute(ctx context.Context, req xattr.ValidateAt return } - err := davinci.ValidFlowsInfoExport([]byte(v.ValueString()), davinci.ExportCmpOpts{}) - if err == nil { + var flows davinci.Flows + + // should really use the actual validator + err := davinci.Unmarshal([]byte(v.ValueString()), &flows, davinci.ExportCmpOpts{}) + if err != nil { 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. The export should not including subflows as these should be managed separately, as their own independent flows.\n\n"+ - "Please re-export the DaVinci flow without subflows included.\n", + "A string value was provided that is not valid DaVinci Export JSON for this provider.\n\n"+ + v.parseValidationErrorMessage(err)+"\n", ) - 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: + if len(flows.Flow) > 0 { 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", + "A string value was provided that is not valid DaVinci Export JSON string format. The export should not including subflows as these should be managed separately, as their own independent flows.\n\n"+ + "Please re-export the DaVinci flow without subflows included.\n", ) + return } @@ -142,13 +141,11 @@ func (v ParsedValue) ValidateAttribute(ctx context.Context, req xattr.ValidateAt "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 for this provider.\n\n"+ - v.parseValidationErrorMessage(err) + "\n", + v.parseValidationErrorMessage(err)+"\n", ) return @@ -173,7 +170,7 @@ func (v ParsedValue) ValidateAttribute(ctx context.Context, req xattr.ValidateAt resp.Diagnostics.AddAttributeWarning( req.Path, "DaVinci Export JSON contains unknown properties", - v.parseValidationErrorMessage(err) + "\n", + v.parseValidationErrorMessage(err)+"\n", ) } } @@ -211,25 +208,25 @@ func (v ParsedValue) parseValidationErrorMessage(err error) string { 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) + 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_test.go b/internal/service/davinci/resource_flow_test.go index 9157e28b..e7bb5f32 100644 --- a/internal/service/davinci/resource_flow_test.go +++ b/internal/service/davinci/resource_flow_test.go @@ -1014,8 +1014,6 @@ func testAccResourceFlow_Variables(t *testing.T, withBootstrapConfig bool) { }) } - - func TestAccResourceFlow_Variables_Invalid(t *testing.T) { resourceName := acctest.ResourceNameGen() @@ -1038,7 +1036,7 @@ func TestAccResourceFlow_Variables_Invalid(t *testing.T) { CheckDestroy: davinci.Flow_CheckDestroy(), Steps: []resource.TestStep{ { - Config: fullStepHcl, + Config: fullStepHcl, ExpectError: regexp.MustCompile(`Invalid DaVinci Flow Export String Value`), }, }, @@ -2558,6 +2556,11 @@ func TestAccResourceFlow_BadParameters(t *testing.T) { t.Fatalf("Failed to get HCL: %v", err) } + multipleExportStepHcl, err := testAccResourceFlow_MultipleExport_HCL(resourceName, name, false) + if err != nil { + t.Fatalf("Failed to get HCL: %v", err) + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheckClient(t) @@ -2568,6 +2571,11 @@ func TestAccResourceFlow_BadParameters(t *testing.T) { ErrorCheck: acctest.ErrorCheck(t), CheckDestroy: davinci.Flow_CheckDestroy(), Steps: []resource.TestStep{ + // Configure + { + Config: multipleExportStepHcl, + ExpectError: regexp.MustCompile(`Invalid DaVinci Flow Export String Value`), + }, // Configure { Config: minimalStepHcl, @@ -2593,3 +2601,36 @@ func TestAccResourceFlow_BadParameters(t *testing.T) { }, }) } + +func testAccResourceFlow_MultipleExport_HCL(resourceName, name string, withBootstrapConfig bool) (hcl string, err error) { + + mainFlowJson, err := acctest.ReadFlowJsonFileRaw("flows/multiple-flows.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 + + flow_json = <