diff --git a/web/flow/flow.go b/web/flow/flow.go index 22ee154ac..36025f7ae 100644 --- a/web/flow/flow.go +++ b/web/flow/flow.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" + "github.com/nyaruka/goflow/envs" "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/utils" "github.com/nyaruka/goflow/utils/uuids" @@ -20,6 +21,7 @@ func init() { web.RegisterJSONRoute(http.MethodPost, "/mr/flow/migrate", web.RequireAuthToken(handleMigrate)) web.RegisterJSONRoute(http.MethodPost, "/mr/flow/inspect", web.RequireAuthToken(handleInspect)) web.RegisterJSONRoute(http.MethodPost, "/mr/flow/clone", web.RequireAuthToken(handleClone)) + web.RegisterJSONRoute(http.MethodPost, "/mr/flow/change_language", web.RequireAuthToken(handleChangeLanguage)) } // Migrates a flow to the latest flow specification @@ -128,3 +130,34 @@ func handleClone(ctx context.Context, s *web.Server, r *http.Request) (interface return cloneJSON, http.StatusOK, nil } + +// Changes the language of a flow by replacing the text with a translation. +// +// { +// "language": "spa", +// "flow": { "uuid": "468621a8-32e6-4cd2-afc1-04416f7151f0", "nodes": [...]} +// } +// +type changeLanguageRequest struct { + Language envs.Language `json:"language" validate:"required"` + Flow json.RawMessage `json:"flow" validate:"required"` +} + +func handleChangeLanguage(ctx context.Context, s *web.Server, r *http.Request) (interface{}, int, error) { + request := &changeLanguageRequest{} + if err := utils.UnmarshalAndValidateWithLimit(r.Body, request, web.MaxRequestBytes); err != nil { + return errors.Wrapf(err, "request failed validation"), http.StatusBadRequest, nil + } + + flow, err := goflow.ReadFlow(request.Flow) + if err != nil { + return errors.Wrapf(err, "unable to read flow"), http.StatusUnprocessableEntity, nil + } + + copy, err := flow.ChangeLanguage(request.Language) + if err != nil { + return errors.Wrapf(err, "unable to change flow language"), http.StatusUnprocessableEntity, nil + } + + return copy, http.StatusOK, nil +} diff --git a/web/flow/flow_test.go b/web/flow/flow_test.go index e6d068b23..84c20faec 100644 --- a/web/flow/flow_test.go +++ b/web/flow/flow_test.go @@ -7,6 +7,7 @@ import ( ) func TestServer(t *testing.T) { + web.RunWebTests(t, "testdata/change_language.json") web.RunWebTests(t, "testdata/clone.json") web.RunWebTests(t, "testdata/inspect.json") web.RunWebTests(t, "testdata/migrate.json") diff --git a/web/flow/testdata/change_language.json b/web/flow/testdata/change_language.json new file mode 100644 index 000000000..c33e95684 --- /dev/null +++ b/web/flow/testdata/change_language.json @@ -0,0 +1,686 @@ +[ + { + "label": "illegal method", + "method": "GET", + "path": "/mr/flow/change_language", + "status": 405, + "response": { + "error": "illegal method: GET" + } + }, + { + "label": "error response if language doesn't exist as translation in flow", + "method": "POST", + "path": "/mr/flow/change_language", + "body": { + "language": "kin", + "flow": { + "uuid": "19cad1f2-9110-4271-98d4-1b968bf19410", + "name": "Change Language", + "spec_version": "13.1.0", + "language": "eng", + "type": "messaging", + "revision": 16, + "expire_after_minutes": 10080, + "localization": { + "spa": { + "e42deebf-90fa-4636-81cb-d247a3d3ba75": { + "text": [ + "Cual pastilla?" + ], + "quick_replies": [ + "Roja", + "Azul" + ] + }, + "d1ce3c92-7025-4607-a910-444361a6b9b3": { + "name": [ + "Roja" + ] + }, + "43f7e69e-727d-4cfe-81b8-564e7833052b": { + "name": [ + "Azul" + ] + }, + "3a044264-81d1-4ba7-882a-e98740c8e724": { + "name": [ + "Otro" + ] + }, + "61bc5ed3-e216-4457-8ce5-ad658e697f29": { + "arguments": [ + "rojo", + "roja" + ] + }, + "5f5fa09f-bf88-4719-ba64-cab9cf2f67b5": { + "arguments": [ + "azul" + ] + } + }, + "ara": { + "e42deebf-90fa-4636-81cb-d247a3d3ba75": { + "text": [ + "\u0627\u064a \u062d\u0628\u0648\u0628" + ], + "quick_replies": [ + "\u0623\u062d\u0645\u0631", + "\u0623\u0632\u0631\u0642" + ] + }, + "d1ce3c92-7025-4607-a910-444361a6b9b3": { + "name": [ + "\u0623\u062d\u0645\u0631" + ] + }, + "43f7e69e-727d-4cfe-81b8-564e7833052b": { + "name": [ + "\u0623\u0632\u0631\u0642" + ] + } + } + }, + "nodes": [ + { + "uuid": "3236913b-9b55-4f01-8b4d-549848c27fe8", + "actions": [ + { + "attachments": [], + "text": "Which pill?", + "type": "send_msg", + "quick_replies": [ + "Red", + "Blue" + ], + "uuid": "e42deebf-90fa-4636-81cb-d247a3d3ba75" + } + ], + "exits": [ + { + "uuid": "500d5c80-0af7-45ce-a95e-e9ece647aa53", + "destination_uuid": "51ad5add-269f-439a-a251-a8e14c6099e2" + } + ] + }, + { + "uuid": "51ad5add-269f-439a-a251-a8e14c6099e2", + "actions": [], + "router": { + "type": "switch", + "default_category_uuid": "3a044264-81d1-4ba7-882a-e98740c8e724", + "cases": [ + { + "arguments": [ + "red" + ], + "type": "has_any_word", + "uuid": "61bc5ed3-e216-4457-8ce5-ad658e697f29", + "category_uuid": "d1ce3c92-7025-4607-a910-444361a6b9b3" + }, + { + "arguments": [ + "blue" + ], + "type": "has_any_word", + "uuid": "5f5fa09f-bf88-4719-ba64-cab9cf2f67b5", + "category_uuid": "43f7e69e-727d-4cfe-81b8-564e7833052b" + } + ], + "categories": [ + { + "uuid": "d1ce3c92-7025-4607-a910-444361a6b9b3", + "name": "Red", + "exit_uuid": "18ce1dab-9875-48ab-9e16-695bad91ecef" + }, + { + "uuid": "43f7e69e-727d-4cfe-81b8-564e7833052b", + "name": "Blue", + "exit_uuid": "73b255e2-59ae-454f-902a-abd2cd1e4eab" + }, + { + "uuid": "3a044264-81d1-4ba7-882a-e98740c8e724", + "name": "Other", + "exit_uuid": "bda03d7b-6ff8-46f2-8308-470ba2c1613c" + } + ], + "operand": "@input.text", + "wait": { + "type": "msg" + }, + "result_name": "Pill" + }, + "exits": [ + { + "uuid": "18ce1dab-9875-48ab-9e16-695bad91ecef" + }, + { + "uuid": "73b255e2-59ae-454f-902a-abd2cd1e4eab" + }, + { + "uuid": "bda03d7b-6ff8-46f2-8308-470ba2c1613c", + "destination_uuid": null + } + ] + } + ] + } + }, + "status": 422, + "response": { + "error": "unable to change flow language: no translation exists for kin" + } + }, + { + "label": "error response if language translation is incomplete", + "method": "POST", + "path": "/mr/flow/change_language", + "body": { + "language": "ara", + "flow": { + "uuid": "19cad1f2-9110-4271-98d4-1b968bf19410", + "name": "Change Language", + "spec_version": "13.1.0", + "language": "eng", + "type": "messaging", + "revision": 16, + "expire_after_minutes": 10080, + "localization": { + "spa": { + "e42deebf-90fa-4636-81cb-d247a3d3ba75": { + "text": [ + "Cual pastilla?" + ], + "quick_replies": [ + "Roja", + "Azul" + ] + }, + "d1ce3c92-7025-4607-a910-444361a6b9b3": { + "name": [ + "Roja" + ] + }, + "43f7e69e-727d-4cfe-81b8-564e7833052b": { + "name": [ + "Azul" + ] + }, + "3a044264-81d1-4ba7-882a-e98740c8e724": { + "name": [ + "Otro" + ] + }, + "61bc5ed3-e216-4457-8ce5-ad658e697f29": { + "arguments": [ + "rojo", + "roja" + ] + }, + "5f5fa09f-bf88-4719-ba64-cab9cf2f67b5": { + "arguments": [ + "azul" + ] + } + }, + "ara": { + "e42deebf-90fa-4636-81cb-d247a3d3ba75": { + "text": [ + "\u0627\u064a \u062d\u0628\u0648\u0628" + ], + "quick_replies": [ + "\u0623\u062d\u0645\u0631", + "\u0623\u0632\u0631\u0642" + ] + }, + "d1ce3c92-7025-4607-a910-444361a6b9b3": { + "name": [ + "\u0623\u062d\u0645\u0631" + ] + }, + "43f7e69e-727d-4cfe-81b8-564e7833052b": { + "name": [ + "\u0623\u0632\u0631\u0642" + ] + } + } + }, + "nodes": [ + { + "uuid": "3236913b-9b55-4f01-8b4d-549848c27fe8", + "actions": [ + { + "attachments": [], + "text": "Which pill?", + "type": "send_msg", + "quick_replies": [ + "Red", + "Blue" + ], + "uuid": "e42deebf-90fa-4636-81cb-d247a3d3ba75" + } + ], + "exits": [ + { + "uuid": "500d5c80-0af7-45ce-a95e-e9ece647aa53", + "destination_uuid": "51ad5add-269f-439a-a251-a8e14c6099e2" + } + ] + }, + { + "uuid": "51ad5add-269f-439a-a251-a8e14c6099e2", + "actions": [], + "router": { + "type": "switch", + "default_category_uuid": "3a044264-81d1-4ba7-882a-e98740c8e724", + "cases": [ + { + "arguments": [ + "red" + ], + "type": "has_any_word", + "uuid": "61bc5ed3-e216-4457-8ce5-ad658e697f29", + "category_uuid": "d1ce3c92-7025-4607-a910-444361a6b9b3" + }, + { + "arguments": [ + "blue" + ], + "type": "has_any_word", + "uuid": "5f5fa09f-bf88-4719-ba64-cab9cf2f67b5", + "category_uuid": "43f7e69e-727d-4cfe-81b8-564e7833052b" + } + ], + "categories": [ + { + "uuid": "d1ce3c92-7025-4607-a910-444361a6b9b3", + "name": "Red", + "exit_uuid": "18ce1dab-9875-48ab-9e16-695bad91ecef" + }, + { + "uuid": "43f7e69e-727d-4cfe-81b8-564e7833052b", + "name": "Blue", + "exit_uuid": "73b255e2-59ae-454f-902a-abd2cd1e4eab" + }, + { + "uuid": "3a044264-81d1-4ba7-882a-e98740c8e724", + "name": "Other", + "exit_uuid": "bda03d7b-6ff8-46f2-8308-470ba2c1613c" + } + ], + "operand": "@input.text", + "wait": { + "type": "msg" + }, + "result_name": "Pill" + }, + "exits": [ + { + "uuid": "18ce1dab-9875-48ab-9e16-695bad91ecef" + }, + { + "uuid": "73b255e2-59ae-454f-902a-abd2cd1e4eab" + }, + { + "uuid": "bda03d7b-6ff8-46f2-8308-470ba2c1613c", + "destination_uuid": null + } + ] + } + ] + } + }, + "status": 422, + "response": { + "error": "unable to change flow language: missing ara translation for text at 61bc5ed3-e216-4457-8ce5-ad658e697f29/arguments, 5f5fa09f-bf88-4719-ba64-cab9cf2f67b5/arguments, 3a044264-81d1-4ba7-882a-e98740c8e724/name" + } + }, + { + "label": "flow response with language changed if translation complete", + "method": "POST", + "path": "/mr/flow/change_language", + "body": { + "language": "spa", + "flow": { + "uuid": "19cad1f2-9110-4271-98d4-1b968bf19410", + "name": "Change Language", + "spec_version": "13.1.0", + "language": "eng", + "type": "messaging", + "revision": 16, + "expire_after_minutes": 10080, + "localization": { + "spa": { + "e42deebf-90fa-4636-81cb-d247a3d3ba75": { + "text": [ + "Cual pastilla?" + ], + "quick_replies": [ + "Roja", + "Azul" + ] + }, + "d1ce3c92-7025-4607-a910-444361a6b9b3": { + "name": [ + "Roja" + ] + }, + "43f7e69e-727d-4cfe-81b8-564e7833052b": { + "name": [ + "Azul" + ] + }, + "3a044264-81d1-4ba7-882a-e98740c8e724": { + "name": [ + "Otro" + ] + }, + "61bc5ed3-e216-4457-8ce5-ad658e697f29": { + "arguments": [ + "rojo", + "roja" + ] + }, + "5f5fa09f-bf88-4719-ba64-cab9cf2f67b5": { + "arguments": [ + "azul" + ] + } + }, + "ara": { + "e42deebf-90fa-4636-81cb-d247a3d3ba75": { + "text": [ + "\u0627\u064a \u062d\u0628\u0648\u0628" + ], + "quick_replies": [ + "\u0623\u062d\u0645\u0631", + "\u0623\u0632\u0631\u0642" + ] + }, + "d1ce3c92-7025-4607-a910-444361a6b9b3": { + "name": [ + "\u0623\u062d\u0645\u0631" + ] + }, + "43f7e69e-727d-4cfe-81b8-564e7833052b": { + "name": [ + "\u0623\u0632\u0631\u0642" + ] + } + } + }, + "nodes": [ + { + "uuid": "3236913b-9b55-4f01-8b4d-549848c27fe8", + "actions": [ + { + "attachments": [], + "text": "Which pill?", + "type": "send_msg", + "quick_replies": [ + "Red", + "Blue" + ], + "uuid": "e42deebf-90fa-4636-81cb-d247a3d3ba75" + } + ], + "exits": [ + { + "uuid": "500d5c80-0af7-45ce-a95e-e9ece647aa53", + "destination_uuid": "51ad5add-269f-439a-a251-a8e14c6099e2" + } + ] + }, + { + "uuid": "51ad5add-269f-439a-a251-a8e14c6099e2", + "actions": [], + "router": { + "type": "switch", + "default_category_uuid": "3a044264-81d1-4ba7-882a-e98740c8e724", + "cases": [ + { + "arguments": [ + "red" + ], + "type": "has_any_word", + "uuid": "61bc5ed3-e216-4457-8ce5-ad658e697f29", + "category_uuid": "d1ce3c92-7025-4607-a910-444361a6b9b3" + }, + { + "arguments": [ + "blue" + ], + "type": "has_any_word", + "uuid": "5f5fa09f-bf88-4719-ba64-cab9cf2f67b5", + "category_uuid": "43f7e69e-727d-4cfe-81b8-564e7833052b" + } + ], + "categories": [ + { + "uuid": "d1ce3c92-7025-4607-a910-444361a6b9b3", + "name": "Red", + "exit_uuid": "18ce1dab-9875-48ab-9e16-695bad91ecef" + }, + { + "uuid": "43f7e69e-727d-4cfe-81b8-564e7833052b", + "name": "Blue", + "exit_uuid": "73b255e2-59ae-454f-902a-abd2cd1e4eab" + }, + { + "uuid": "3a044264-81d1-4ba7-882a-e98740c8e724", + "name": "Other", + "exit_uuid": "bda03d7b-6ff8-46f2-8308-470ba2c1613c" + } + ], + "operand": "@input.text", + "wait": { + "type": "msg" + }, + "result_name": "Pill" + }, + "exits": [ + { + "uuid": "18ce1dab-9875-48ab-9e16-695bad91ecef" + }, + { + "uuid": "73b255e2-59ae-454f-902a-abd2cd1e4eab" + }, + { + "uuid": "bda03d7b-6ff8-46f2-8308-470ba2c1613c", + "destination_uuid": null + } + ] + } + ] + } + }, + "status": 200, + "response": { + "uuid": "19cad1f2-9110-4271-98d4-1b968bf19410", + "name": "Change Language", + "spec_version": "13.1.0", + "language": "spa", + "type": "messaging", + "revision": 16, + "expire_after_minutes": 10080, + "localization": { + "ara": { + "43f7e69e-727d-4cfe-81b8-564e7833052b": { + "name": [ + "أزرق" + ] + }, + "d1ce3c92-7025-4607-a910-444361a6b9b3": { + "name": [ + "أحمر" + ] + }, + "e42deebf-90fa-4636-81cb-d247a3d3ba75": { + "quick_replies": [ + "أحمر", + "أزرق" + ], + "text": [ + "اي حبوب" + ] + } + }, + "eng": { + "3a044264-81d1-4ba7-882a-e98740c8e724": { + "name": [ + "Other" + ] + }, + "43f7e69e-727d-4cfe-81b8-564e7833052b": { + "name": [ + "Blue" + ] + }, + "5f5fa09f-bf88-4719-ba64-cab9cf2f67b5": { + "arguments": [ + "blue" + ] + }, + "61bc5ed3-e216-4457-8ce5-ad658e697f29": { + "arguments": [ + "red" + ] + }, + "d1ce3c92-7025-4607-a910-444361a6b9b3": { + "name": [ + "Red" + ] + }, + "e42deebf-90fa-4636-81cb-d247a3d3ba75": { + "quick_replies": [ + "Red", + "Blue" + ], + "text": [ + "Which pill?" + ] + } + }, + "spa": { + "3a044264-81d1-4ba7-882a-e98740c8e724": { + "name": [ + "Otro" + ] + }, + "43f7e69e-727d-4cfe-81b8-564e7833052b": { + "name": [ + "Azul" + ] + }, + "5f5fa09f-bf88-4719-ba64-cab9cf2f67b5": { + "arguments": [ + "azul" + ] + }, + "61bc5ed3-e216-4457-8ce5-ad658e697f29": { + "arguments": [ + "rojo", + "roja" + ] + }, + "d1ce3c92-7025-4607-a910-444361a6b9b3": { + "name": [ + "Roja" + ] + }, + "e42deebf-90fa-4636-81cb-d247a3d3ba75": { + "quick_replies": [ + "Roja", + "Azul" + ], + "text": [ + "Cual pastilla?" + ] + } + } + }, + "nodes": [ + { + "uuid": "3236913b-9b55-4f01-8b4d-549848c27fe8", + "actions": [ + { + "type": "send_msg", + "uuid": "e42deebf-90fa-4636-81cb-d247a3d3ba75", + "text": "Cual pastilla?", + "quick_replies": [ + "Roja", + "Azul" + ] + } + ], + "exits": [ + { + "uuid": "500d5c80-0af7-45ce-a95e-e9ece647aa53", + "destination_uuid": "51ad5add-269f-439a-a251-a8e14c6099e2" + } + ] + }, + { + "uuid": "51ad5add-269f-439a-a251-a8e14c6099e2", + "router": { + "type": "switch", + "wait": { + "type": "msg" + }, + "result_name": "Pill", + "categories": [ + { + "uuid": "d1ce3c92-7025-4607-a910-444361a6b9b3", + "name": "Roja", + "exit_uuid": "18ce1dab-9875-48ab-9e16-695bad91ecef" + }, + { + "uuid": "43f7e69e-727d-4cfe-81b8-564e7833052b", + "name": "Azul", + "exit_uuid": "73b255e2-59ae-454f-902a-abd2cd1e4eab" + }, + { + "uuid": "3a044264-81d1-4ba7-882a-e98740c8e724", + "name": "Otro", + "exit_uuid": "bda03d7b-6ff8-46f2-8308-470ba2c1613c" + } + ], + "operand": "@input.text", + "cases": [ + { + "uuid": "61bc5ed3-e216-4457-8ce5-ad658e697f29", + "type": "has_any_word", + "arguments": [ + "rojo", + "roja" + ], + "category_uuid": "d1ce3c92-7025-4607-a910-444361a6b9b3" + }, + { + "uuid": "5f5fa09f-bf88-4719-ba64-cab9cf2f67b5", + "type": "has_any_word", + "arguments": [ + "azul" + ], + "category_uuid": "43f7e69e-727d-4cfe-81b8-564e7833052b" + } + ], + "default_category_uuid": "3a044264-81d1-4ba7-882a-e98740c8e724" + }, + "exits": [ + { + "uuid": "18ce1dab-9875-48ab-9e16-695bad91ecef" + }, + { + "uuid": "73b255e2-59ae-454f-902a-abd2cd1e4eab" + }, + { + "uuid": "bda03d7b-6ff8-46f2-8308-470ba2c1613c" + } + ] + } + ] + } + } +] \ No newline at end of file