From 0f972ec1a141e80d0fe497c80bbde433eca951de Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Tue, 18 Jul 2023 13:40:31 -0500 Subject: [PATCH] Limit how old surveyor submissions can be --- web/surveyor/submit.go | 11 + web/surveyor/submit_test.go | 159 ++++-- web/surveyor/testdata/too_old.json | 473 ++++++++++++++++++ ...submission.json => valid_submission1.json} | 0 ...ubmission2.json => valid_submission2.json} | 0 5 files changed, 591 insertions(+), 52 deletions(-) create mode 100644 web/surveyor/testdata/too_old.json rename web/surveyor/testdata/{contact_surveyor_submission.json => valid_submission1.json} (100%) rename web/surveyor/testdata/{contact_surveyor_submission2.json => valid_submission2.json} (100%) diff --git a/web/surveyor/submit.go b/web/surveyor/submit.go index 6f571def3..0fab85283 100644 --- a/web/surveyor/submit.go +++ b/web/surveyor/submit.go @@ -4,7 +4,9 @@ import ( "context" "encoding/json" "net/http" + "time" + "github.com/nyaruka/gocommon/dates" "github.com/nyaruka/gocommon/urns" "github.com/nyaruka/goflow/assets" "github.com/nyaruka/goflow/envs" @@ -19,6 +21,10 @@ import ( "github.com/pkg/errors" ) +const ( + maxSubmissionAge = time.Hour * 24 * 90 +) + func init() { web.RegisterRoute(http.MethodPost, "/mr/surveyor/submit", web.RequireUserToken(web.MarshaledResponse(handleSubmit))) } @@ -72,6 +78,11 @@ func handleSubmit(ctx context.Context, rt *runtime.Runtime, r *http.Request) (an return nil, 0, errors.Wrapf(err, "error reading session") } + // reject any really old sessions as this could create messages/runs outside of the archival period + if dates.Since(fs.Trigger().TriggeredOn()) > maxSubmissionAge { + return nil, 0, errors.New("session too old to be submitted") + } + // and our events sessionEvents := make([]flows.Event, 0, len(request.Events)) for _, e := range request.Events { diff --git a/web/surveyor/submit_test.go b/web/surveyor/submit_test.go index d4e5f8baa..149e8abb1 100644 --- a/web/surveyor/submit_test.go +++ b/web/surveyor/submit_test.go @@ -8,8 +8,10 @@ import ( "path/filepath" "sync" "testing" + "time" "github.com/buger/jsonparser" + "github.com/nyaruka/gocommon/dates" "github.com/nyaruka/goflow/assets" "github.com/nyaruka/goflow/flows" _ "github.com/nyaruka/mailroom/core/handlers" @@ -41,57 +43,107 @@ func TestSurveyor(t *testing.T) { } tcs := []struct { - File string - Token string - StatusCode int - Contains string - Assertions []Assertion + file string + token string + expectedStatus int + expectedContains string + assertions []Assertion }{ - {"contact_surveyor_submission.json", "", 401, "missing authorization", nil}, - {"contact_surveyor_submission.json", "invalid", 401, "invalid authorization", []Assertion{ - {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id`, 0}, - }}, + { + file: "valid_submission1.json", + token: "", + expectedStatus: 401, + expectedContains: "missing authorization", + }, + { + file: "valid_submission1.json", + token: "invalid", + expectedStatus: 401, + expectedContains: "invalid authorization", + assertions: []Assertion{ + {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id`, 0}, + }, + }, // new contact is created (our test db already has a bob, he should be unaffected) - {"contact_surveyor_submission.json", "sesame", 201, `"status": "C"`, []Assertion{ - {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id AND contact_id = :contact_id AND status = 'C'`, 1}, - {`SELECT count(*) FROM contacts_contact WHERE name = 'Bob' AND org_id = 1`, 2}, - {`SELECT count(*) FROM contacts_contact WHERE uuid = 'bdfe862c-84f8-422e-8fdc-ebfaaae0697a'`, 0}, - {`SELECT count(*) FROM contacts_contact WHERE name = 'Bob' AND fields -> :age_field_uuid = jsonb_build_object('text', '37', 'number', 37)`, 1}, - {`SELECT count(*) FROM contacts_contacturn WHERE identity = 'tel::+593979123456' AND contact_id = :contact_id`, 1}, - {`SELECT count(*) FROM contacts_contactgroup_contacts WHERE contact_id = :contact_id and contactgroup_id = :testers_group_id`, 1}, - {`SELECT count(*) FROM msgs_msg WHERE contact_id = :contact_id AND contact_urn_id IS NULL AND direction = 'O' AND org_id = :org_id`, 4}, - {`SELECT count(*) FROM msgs_msg WHERE contact_id = :contact_id AND contact_urn_id IS NULL AND direction = 'I' AND org_id = :org_id`, 3}, - }}, + { + file: "valid_submission1.json", + token: "sesame", + expectedStatus: 201, + expectedContains: `"status": "C"`, + assertions: []Assertion{ + {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id AND contact_id = :contact_id AND status = 'C'`, 1}, + {`SELECT count(*) FROM contacts_contact WHERE name = 'Bob' AND org_id = 1`, 2}, + {`SELECT count(*) FROM contacts_contact WHERE uuid = 'bdfe862c-84f8-422e-8fdc-ebfaaae0697a'`, 0}, + {`SELECT count(*) FROM contacts_contact WHERE name = 'Bob' AND fields -> :age_field_uuid = jsonb_build_object('text', '37', 'number', 37)`, 1}, + {`SELECT count(*) FROM contacts_contacturn WHERE identity = 'tel::+593979123456' AND contact_id = :contact_id`, 1}, + {`SELECT count(*) FROM contacts_contactgroup_contacts WHERE contact_id = :contact_id and contactgroup_id = :testers_group_id`, 1}, + {`SELECT count(*) FROM msgs_msg WHERE contact_id = :contact_id AND contact_urn_id IS NULL AND direction = 'O' AND org_id = :org_id`, 4}, + {`SELECT count(*) FROM msgs_msg WHERE contact_id = :contact_id AND contact_urn_id IS NULL AND direction = 'I' AND org_id = :org_id`, 3}, + }, + }, // dupe submission should fail due to run UUIDs being duplicated - {"contact_surveyor_submission.json", "sesame", 500, `error writing runs`, []Assertion{ - {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id`, 1}, - }}, + { + file: "valid_submission1.json", + token: "sesame", + expectedStatus: 500, + expectedContains: `error writing runs`, + assertions: []Assertion{ + {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id`, 1}, + }, + }, // but submission with new UUIDs should succeed, new run is created but not contact - {"contact_surveyor_submission2.json", "sesame", 201, `"status": "C"`, []Assertion{ - {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id AND contact_id = :contact_id`, 2}, - {`SELECT count(*) FROM contacts_contact WHERE uuid = 'bdfe862c-84f8-422e-8fdc-ebfaaae0697a'`, 0}, - {`SELECT count(*) FROM contacts_contact WHERE name = 'Bob' AND fields -> :age_field_uuid = jsonb_build_object('text', '37', 'number', 37)`, 1}, - {`SELECT count(*) FROM contacts_contacturn WHERE identity = 'tel::+593979123456' AND contact_id = :contact_id`, 1}, - {`SELECT count(*) FROM contacts_contactgroup_contacts WHERE contact_id = :contact_id and contactgroup_id = :testers_group_id`, 1}, - {`SELECT count(*) FROM msgs_msg WHERE contact_id = :contact_id AND contact_urn_id IS NULL AND direction = 'O' AND org_id = :org_id`, 8}, - {`SELECT count(*) FROM msgs_msg WHERE contact_id = :contact_id AND contact_urn_id IS NULL AND direction = 'I' AND org_id = :org_id`, 6}, - }}, + { + file: "valid_submission2.json", + token: "sesame", + expectedStatus: 201, + expectedContains: `"status": "C"`, + assertions: []Assertion{ + {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id AND contact_id = :contact_id`, 2}, + {`SELECT count(*) FROM contacts_contact WHERE uuid = 'bdfe862c-84f8-422e-8fdc-ebfaaae0697a'`, 0}, + {`SELECT count(*) FROM contacts_contact WHERE name = 'Bob' AND fields -> :age_field_uuid = jsonb_build_object('text', '37', 'number', 37)`, 1}, + {`SELECT count(*) FROM contacts_contacturn WHERE identity = 'tel::+593979123456' AND contact_id = :contact_id`, 1}, + {`SELECT count(*) FROM contacts_contactgroup_contacts WHERE contact_id = :contact_id and contactgroup_id = :testers_group_id`, 1}, + {`SELECT count(*) FROM msgs_msg WHERE contact_id = :contact_id AND contact_urn_id IS NULL AND direction = 'O' AND org_id = :org_id`, 8}, + {`SELECT count(*) FROM msgs_msg WHERE contact_id = :contact_id AND contact_urn_id IS NULL AND direction = 'I' AND org_id = :org_id`, 6}, + }}, // group removal is ONLY in the modifier - {"remove_group.json", "sesame", 201, `"status": "C"`, []Assertion{ - {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id AND contact_id = :contact_id`, 3}, - {`SELECT count(*) FROM contacts_contact WHERE uuid = 'bdfe862c-84f8-422e-8fdc-ebfaaae0697a'`, 0}, - {`SELECT count(*) FROM contacts_contact WHERE name = 'Bob' AND fields -> :age_field_uuid = jsonb_build_object('text', '37', 'number', 37)`, 1}, - {`SELECT count(*) FROM contacts_contacturn WHERE identity = 'tel::+593979123456' AND contact_id = :contact_id`, 1}, - {`SELECT count(*) FROM contacts_contactgroup_contacts WHERE contact_id = :contact_id and contactgroup_id = :testers_group_id`, 0}, - }}, + { + file: "remove_group.json", + token: "sesame", + expectedStatus: 201, + expectedContains: `"status": "C"`, + assertions: []Assertion{ + {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id AND contact_id = :contact_id`, 3}, + {`SELECT count(*) FROM contacts_contact WHERE uuid = 'bdfe862c-84f8-422e-8fdc-ebfaaae0697a'`, 0}, + {`SELECT count(*) FROM contacts_contact WHERE name = 'Bob' AND fields -> :age_field_uuid = jsonb_build_object('text', '37', 'number', 37)`, 1}, + {`SELECT count(*) FROM contacts_contacturn WHERE identity = 'tel::+593979123456' AND contact_id = :contact_id`, 1}, + {`SELECT count(*) FROM contacts_contactgroup_contacts WHERE contact_id = :contact_id and contactgroup_id = :testers_group_id`, 0}, + }, + }, // new contact, new session, group and field no longer exist - {"missing_group_field.json", "sesame", 201, `"status": "C"`, []Assertion{ - {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id AND contact_id = :contact_id`, 1}, - {`SELECT count(*) FROM contacts_contact WHERE uuid = 'c7fa24ca-48f9-45bf-b923-f95aa49c3cd2'`, 0}, - {`SELECT count(*) FROM contacts_contact WHERE name = 'Fred' AND fields = jsonb_build_object()`, 1}, - {`SELECT count(*) FROM contacts_contacturn WHERE identity = 'tel::+593979123488' AND contact_id = :contact_id`, 1}, - {`SELECT count(*) FROM contacts_contactgroup_contacts WHERE contact_id = :contact_id and contactgroup_id = :testers_group_id`, 0}, - }}, + { + file: "missing_group_field.json", + token: "sesame", + expectedStatus: 201, + expectedContains: `"status": "C"`, + assertions: []Assertion{ + {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id AND contact_id = :contact_id`, 1}, + {`SELECT count(*) FROM contacts_contact WHERE uuid = 'c7fa24ca-48f9-45bf-b923-f95aa49c3cd2'`, 0}, + {`SELECT count(*) FROM contacts_contact WHERE name = 'Fred' AND fields = jsonb_build_object()`, 1}, + {`SELECT count(*) FROM contacts_contacturn WHERE identity = 'tel::+593979123488' AND contact_id = :contact_id`, 1}, + {`SELECT count(*) FROM contacts_contactgroup_contacts WHERE contact_id = :contact_id and contactgroup_id = :testers_group_id`, 0}, + }, + }, + // submission that is too old should fail + { + file: "too_old.json", + token: "sesame", + expectedStatus: 500, + expectedContains: `"error": "session too old to be submitted"`, + assertions: []Assertion{ + {`SELECT count(*) FROM flows_flowrun WHERE flow_id = :flow_id AND contact_id = :contact_id`, 0}, + }, + }, } type AssertionArgs struct { @@ -109,9 +161,12 @@ func TestSurveyor(t *testing.T) { TestersGroupID: testdata.TestersGroup.ID, } + dates.SetNowSource(dates.NewSequentialNowSource(time.Date(2018, 12, 21, 12, 0, 0, 0, time.UTC))) + defer dates.SetNowSource(dates.DefaultNowSource) + for i, tc := range tcs { - testID := fmt.Sprintf("%s[token=%s]", tc.File, tc.Token) - path := filepath.Join("testdata", tc.File) + testID := fmt.Sprintf("%s[token=%s]", tc.file, tc.token) + path := filepath.Join("testdata", tc.file) submission := testsuite.ReadFile(path) url := "http://localhost:8090/mr/surveyor/submit" @@ -119,22 +174,22 @@ func TestSurveyor(t *testing.T) { assert.NoError(t, err) req.Header.Set("Content-Type", "application/json") - if tc.Token != "" { - req.Header.Set("Authorization", "Token "+tc.Token) + if tc.token != "" { + req.Header.Set("Authorization", "Token "+tc.token) } resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) - assert.Equal(t, tc.StatusCode, resp.StatusCode, "unexpected status code for %s", testID) + assert.Equal(t, tc.expectedStatus, resp.StatusCode, "unexpected status code for %s", testID) body, _ := io.ReadAll(resp.Body) - assert.Containsf(t, string(body), tc.Contains, "%s does not contain expected body", testID) + assert.Containsf(t, string(body), tc.expectedContains, "%s does not contain expected body", testID) id, _ := jsonparser.GetInt(body, "contact", "id") args.ContactID = flows.ContactID(id) // if we have assertions, check them - for ii, assertion := range tc.Assertions { + for ii, assertion := range tc.assertions { rows, err := rt.DB.NamedQuery(assertion.Query, args) assert.NoError(t, err, "%d:%d error with named query", i, ii) diff --git a/web/surveyor/testdata/too_old.json b/web/surveyor/testdata/too_old.json new file mode 100644 index 000000000..d50de289a --- /dev/null +++ b/web/surveyor/testdata/too_old.json @@ -0,0 +1,473 @@ +{ + "session": { + "type": "messaging_offline", + "environment": { + "date_format": "DD-MM-YYYY", + "time_format": "tt:mm", + "timezone": "Africa/Kigali", + "default_language": "eng", + "allowed_languages": [ + "eng", + "fra" + ], + "redaction_policy": "none" + }, + "trigger": { + "type": "manual", + "environment": { + "date_format": "DD-MM-YYYY", + "time_format": "tt:mm", + "timezone": "Africa/Kigali", + "default_language": "eng", + "allowed_languages": [ + "eng", + "fra" + ], + "redaction_policy": "none" + }, + "flow": { + "uuid": "ed8cf8d4-a42c-4ce1-a7e3-44a2918e3cec", + "name": "Contact Details" + }, + "contact": { + "uuid": "bdfe862c-84f8-422e-8fdc-ebfaaae0697a", + "created_on": "2017-12-18T19:14:53.707395Z" + }, + "triggered_on": "2017-12-18T19:14:53.70753Z" + }, + "contact": { + "uuid": "bdfe862c-84f8-422e-8fdc-ebfaaae0697a", + "name": "Bob", + "created_on": "2017-12-18T19:14:53.707395Z", + "urns": [ + "tel:+593979123456" + ], + "groups": [ + { + "uuid": "5e9d8fab-5e7e-4f51-b533-261af5dea70d", + "name": "Testers" + } + ], + "fields": { + "age": { + "text": "37", + "number": 37 + } + } + }, + "runs": [ + { + "uuid": "1d4f5427-a7fa-4d4c-9c70-93a73fca59da", + "flow": { + "uuid": "ed8cf8d4-a42c-4ce1-a7e3-44a2918e3cec", + "name": "Contact Details" + }, + "path": [ + { + "uuid": "a6c7ff4d-b23f-450a-ac16-da870f6f73be", + "node_uuid": "036901e0-abb8-4979-92cb-f0d43aeb5b68", + "exit_uuid": "706853c2-831b-4dd8-8073-cd51b21d94d6", + "arrived_on": "2017-12-18T19:14:53.711805Z" + }, + { + "uuid": "dd2c2662-437f-480c-93e5-a5150ca67d1f", + "node_uuid": "39fe1ce0-7dee-445e-9945-48c72a05cef5", + "exit_uuid": "bb999ff8-5eb3-45f6-bec6-a0430105b0ca", + "arrived_on": "2017-12-18T19:14:53.712041Z" + }, + { + "uuid": "1d0cc3ea-f5c6-409f-af1f-a9bd26c1a367", + "node_uuid": "73dda1a7-9152-45f1-993a-e7d01eb028db", + "exit_uuid": "5e7a398e-eebe-4b32-8600-374659f56d9e", + "arrived_on": "2017-12-18T19:14:55.289318Z" + }, + { + "uuid": "16c63d0d-2eaa-4a60-bc9c-b9358b8739f0", + "node_uuid": "1a7612b5-777d-4af3-a657-077c46f242d9", + "exit_uuid": "0c047d03-3b61-4ff2-8bc8-43a89cf1087b", + "arrived_on": "2017-12-18T19:14:55.289954Z" + }, + { + "uuid": "dac7fb6e-5e9d-4793-be36-b5e8756a6f6b", + "node_uuid": "2d55c61f-384c-4a07-a17e-1e42fc543dd9", + "exit_uuid": "4e483159-af9f-48a4-907f-c875fde66c70", + "arrived_on": "2017-12-18T19:14:56.721324Z" + }, + { + "uuid": "a034eccc-581a-4b84-8a6f-37ed513b53d0", + "node_uuid": "52a6784b-f51f-42c7-8c6a-3e5ec42603bb", + "exit_uuid": "34d09b52-ac85-44a4-b4f4-c7a3b489fcf8", + "arrived_on": "2017-12-18T19:14:56.730637Z" + }, + { + "uuid": "94106b99-9e4d-4bfa-9feb-dcd780d7bf89", + "node_uuid": "6d5703f9-938c-4c2f-9cc7-7d1bbe328095", + "exit_uuid": "6bab242d-85d5-4afe-b6e7-5fe7c98f187e", + "arrived_on": "2017-12-18T19:14:57.973998Z" + } + ], + "events": [ + { + "type": "msg_created", + "created_on": "2017-12-18T19:14:53.711924Z", + "step_uuid": "a6c7ff4d-b23f-450a-ac16-da870f6f73be", + "msg": { + "uuid": "c7c3e345-dcb9-487d-9102-b1cbc7deacf1", + "text": "Hi there. What's your name?" + } + }, + { + "type": "msg_wait", + "created_on": "2017-12-18T19:14:53.712575Z", + "step_uuid": "dd2c2662-437f-480c-93e5-a5150ca67d1f" + }, + { + "type": "msg_received", + "created_on": "2017-12-18T19:14:55.288681Z", + "step_uuid": "dd2c2662-437f-480c-93e5-a5150ca67d1f", + "msg": { + "uuid": "2dcb9bbc-46f7-4bc6-afe9-3887209402cc", + "text": "Bob" + } + }, + { + "type": "run_result_changed", + "created_on": "2017-12-18T19:14:55.289221Z", + "step_uuid": "dd2c2662-437f-480c-93e5-a5150ca67d1f", + "name": "Name", + "value": "Bob", + "category": "All Responses", + "input": "Bob" + }, + { + "type": "contact_name_changed", + "created_on": "2017-12-18T19:14:55.289471Z", + "step_uuid": "1d0cc3ea-f5c6-409f-af1f-a9bd26c1a367", + "name": "Bob" + }, + { + "type": "msg_created", + "created_on": "2017-12-18T19:14:55.289645Z", + "step_uuid": "1d0cc3ea-f5c6-409f-af1f-a9bd26c1a367", + "msg": { + "uuid": "b6fbd0e6-35ce-495a-b3f0-8c5f9852452f", + "text": "Thanks Bob. What's your phone number?" + } + }, + { + "type": "msg_wait", + "created_on": "2017-12-18T19:14:55.290128Z", + "step_uuid": "16c63d0d-2eaa-4a60-bc9c-b9358b8739f0" + }, + { + "type": "msg_received", + "created_on": "2017-12-18T19:14:56.720739Z", + "step_uuid": "16c63d0d-2eaa-4a60-bc9c-b9358b8739f0", + "msg": { + "uuid": "ca87bcce-f113-4ad2-9c5b-96efbc0d6248", + "text": "+593979123456" + } + }, + { + "type": "run_result_changed", + "created_on": "2017-12-18T19:14:56.721229Z", + "step_uuid": "16c63d0d-2eaa-4a60-bc9c-b9358b8739f0", + "name": "Phone", + "value": "+593979123456", + "category": "phone", + "input": "+593979123456" + }, + { + "type": "contact_urns_changed", + "created_on": "2017-12-18T19:14:56.730115Z", + "step_uuid": "dac7fb6e-5e9d-4793-be36-b5e8756a6f6b", + "urns": [ + "tel:+593979123456" + ] + }, + { + "type": "contact_groups_changed", + "created_on": "2017-12-18T19:14:56.730233Z", + "step_uuid": "dac7fb6e-5e9d-4793-be36-b5e8756a6f6b", + "groups_added": [ + { + "uuid": "5e9d8fab-5e7e-4f51-b533-261af5dea70d", + "name": "Testers" + } + ] + }, + { + "type": "msg_created", + "created_on": "2017-12-18T19:14:56.730535Z", + "step_uuid": "dac7fb6e-5e9d-4793-be36-b5e8756a6f6b", + "msg": { + "uuid": "0c578642-8236-4b71-94a8-5f1db5bd3764", + "text": "Finally, what is your age?" + } + }, + { + "type": "msg_wait", + "created_on": "2017-12-18T19:14:56.73068Z", + "step_uuid": "a034eccc-581a-4b84-8a6f-37ed513b53d0" + }, + { + "type": "msg_received", + "created_on": "2017-12-18T19:14:57.97173Z", + "step_uuid": "a034eccc-581a-4b84-8a6f-37ed513b53d0", + "msg": { + "uuid": "fc7f9830-ec40-4a43-b92e-6eb04750c119", + "text": "37" + } + }, + { + "type": "run_result_changed", + "created_on": "2017-12-18T19:14:57.973844Z", + "step_uuid": "a034eccc-581a-4b84-8a6f-37ed513b53d0", + "name": "Age", + "value": "37", + "category": "numeric", + "input": "37" + }, + { + "type": "contact_field_changed", + "created_on": "2017-12-18T19:14:57.974455Z", + "step_uuid": "94106b99-9e4d-4bfa-9feb-dcd780d7bf89", + "field": { + "key": "age", + "name": "Age" + }, + "value": { + "text": "37", + "number": 37 + } + }, + { + "type": "msg_created", + "created_on": "2017-12-18T19:14:57.975302Z", + "step_uuid": "94106b99-9e4d-4bfa-9feb-dcd780d7bf89", + "msg": { + "uuid": "388e5968-57c8-4795-b4bf-d73916aa5f45", + "text": "Thanks Bob. You are 37 and your phone number is 097 912 3456" + } + } + ], + "results": { + "age": { + "name": "Age", + "value": "37", + "category": "numeric", + "node_uuid": "52a6784b-f51f-42c7-8c6a-3e5ec42603bb", + "input": "37", + "created_on": "2017-12-18T19:14:57.972203Z" + }, + "name": { + "name": "Name", + "value": "Bob", + "category": "All Responses", + "node_uuid": "39fe1ce0-7dee-445e-9945-48c72a05cef5", + "input": "Bob", + "created_on": "2017-12-18T19:14:55.288827Z" + }, + "phone": { + "name": "Phone", + "value": "+593979123456", + "category": "phone", + "node_uuid": "1a7612b5-777d-4af3-a657-077c46f242d9", + "input": "+593979123456", + "created_on": "2017-12-18T19:14:56.721077Z" + } + }, + "status": "completed", + "created_on": "2017-12-18T19:14:53.711676Z", + "modified_on": "2017-12-18T19:14:57.975537Z", + "expires_on": "2017-12-25T19:14:57.97142Z", + "exited_on": "2017-12-18T19:14:57.975537Z" + } + ], + "status": "completed", + "input": { + "type": "msg", + "uuid": "fc7f9830-ec40-4a43-b92e-6eb04750c119", + "created_on": "2017-12-18T19:14:57.971277Z", + "urn": "", + "text": "37" + } + }, + "events": [ + { + "type": "msg_created", + "created_on": "2017-12-18T19:14:53.711924Z", + "step_uuid": "a6c7ff4d-b23f-450a-ac16-da870f6f73be", + "msg": { + "uuid": "c7c3e345-dcb9-487d-9102-b1cbc7deacf1", + "text": "Hi there. What's your name?" + } + }, + { + "type": "msg_wait", + "created_on": "2017-12-18T19:14:53.712575Z", + "step_uuid": "dd2c2662-437f-480c-93e5-a5150ca67d1f" + }, + { + "type": "msg_received", + "created_on": "2017-12-18T19:14:55.288681Z", + "step_uuid": "dd2c2662-437f-480c-93e5-a5150ca67d1f", + "msg": { + "uuid": "2dcb9bbc-46f7-4bc6-afe9-3887209402cc", + "text": "Bob" + } + }, + { + "type": "run_result_changed", + "created_on": "2017-12-18T19:14:55.289221Z", + "step_uuid": "dd2c2662-437f-480c-93e5-a5150ca67d1f", + "name": "Name", + "value": "Bob", + "category": "All Responses", + "input": "Bob" + }, + { + "type": "contact_name_changed", + "created_on": "2017-12-18T19:14:55.289471Z", + "step_uuid": "1d0cc3ea-f5c6-409f-af1f-a9bd26c1a367", + "name": "Bob" + }, + { + "type": "msg_created", + "created_on": "2017-12-18T19:14:55.289645Z", + "step_uuid": "1d0cc3ea-f5c6-409f-af1f-a9bd26c1a367", + "msg": { + "uuid": "b6fbd0e6-35ce-495a-b3f0-8c5f9852452f", + "text": "Thanks Bob. What's your phone number?" + } + }, + { + "type": "msg_wait", + "created_on": "2017-12-18T19:14:55.290128Z", + "step_uuid": "16c63d0d-2eaa-4a60-bc9c-b9358b8739f0" + }, + { + "type": "msg_received", + "created_on": "2017-12-18T19:14:56.720739Z", + "step_uuid": "16c63d0d-2eaa-4a60-bc9c-b9358b8739f0", + "msg": { + "uuid": "ca87bcce-f113-4ad2-9c5b-96efbc0d6248", + "text": "+593979123456" + } + }, + { + "type": "run_result_changed", + "created_on": "2017-12-18T19:14:56.721229Z", + "step_uuid": "16c63d0d-2eaa-4a60-bc9c-b9358b8739f0", + "name": "Phone", + "value": "+593979123456", + "category": "phone", + "input": "+593979123456" + }, + { + "type": "contact_urns_changed", + "created_on": "2017-12-18T19:14:56.730115Z", + "step_uuid": "dac7fb6e-5e9d-4793-be36-b5e8756a6f6b", + "urns": [ + "tel:+593979123456" + ] + }, + { + "type": "contact_groups_changed", + "created_on": "2017-12-18T19:14:56.730233Z", + "step_uuid": "dac7fb6e-5e9d-4793-be36-b5e8756a6f6b", + "groups_added": [ + { + "uuid": "5e9d8fab-5e7e-4f51-b533-261af5dea70d", + "name": "Testers" + } + ] + }, + { + "type": "msg_created", + "created_on": "2017-12-18T19:14:56.730535Z", + "step_uuid": "dac7fb6e-5e9d-4793-be36-b5e8756a6f6b", + "msg": { + "uuid": "0c578642-8236-4b71-94a8-5f1db5bd3764", + "text": "Finally, what is your age?" + } + }, + { + "type": "msg_wait", + "created_on": "2017-12-18T19:14:56.73068Z", + "step_uuid": "a034eccc-581a-4b84-8a6f-37ed513b53d0" + }, + { + "type": "msg_received", + "created_on": "2017-12-18T19:14:57.97173Z", + "step_uuid": "a034eccc-581a-4b84-8a6f-37ed513b53d0", + "msg": { + "uuid": "fc7f9830-ec40-4a43-b92e-6eb04750c119", + "text": "37" + } + }, + { + "type": "run_result_changed", + "created_on": "2017-12-18T19:14:57.973844Z", + "step_uuid": "a034eccc-581a-4b84-8a6f-37ed513b53d0", + "name": "Age", + "value": "37", + "category": "numeric", + "input": "37" + }, + { + "type": "contact_field_changed", + "created_on": "2017-12-18T19:14:57.974455Z", + "step_uuid": "94106b99-9e4d-4bfa-9feb-dcd780d7bf89", + "field": { + "key": "age", + "name": "Age" + }, + "value": { + "text": "37", + "number": 37 + } + }, + { + "type": "msg_created", + "created_on": "2017-12-18T19:14:57.975302Z", + "step_uuid": "94106b99-9e4d-4bfa-9feb-dcd780d7bf89", + "msg": { + "uuid": "388e5968-57c8-4795-b4bf-d73916aa5f45", + "text": "Thanks Bob. You are 37 and your phone number is 097 912 3456" + } + } + ], + "modifiers": [ + { + "type": "name", + "name": "Bob" + }, + { + "type": "urn", + "urn": "tel:+593979123456", + "modification": "append" + }, + { + "type": "groups", + "groups": [ + { + "uuid": "5e9d8fab-5e7e-4f51-b533-261af5dea70d", + "name": "Testers" + } + ], + "modification": "add" + }, + { + "type": "field", + "field": { + "key": "age", + "name": "Age" + }, + "value": { + "text": "37", + "number": 37 + } + } + ] +} \ No newline at end of file diff --git a/web/surveyor/testdata/contact_surveyor_submission.json b/web/surveyor/testdata/valid_submission1.json similarity index 100% rename from web/surveyor/testdata/contact_surveyor_submission.json rename to web/surveyor/testdata/valid_submission1.json diff --git a/web/surveyor/testdata/contact_surveyor_submission2.json b/web/surveyor/testdata/valid_submission2.json similarity index 100% rename from web/surveyor/testdata/contact_surveyor_submission2.json rename to web/surveyor/testdata/valid_submission2.json