diff --git a/.gitignore b/.gitignore index 2a6c761..223874a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ dist/ # Output of the go coverage tool, specifically when used with LiteIDE *.out -fabric \ No newline at end of file +deploy +fabfile.py diff --git a/CHANGELOG.md b/CHANGELOG.md index d6f00d3..2c0c5d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +v7.1.4 +---------- + * Record flow on msgs + +v7.1.3 +---------- + * Remove deletion of recent runs as these are no longer created + +v7.1.2 +---------- + * Use run status instead of is_active and exit_type + * No longer include events in run archives + +v7.1.1 +---------- + * Remove references to flowrun.parent_id which is no longer set by mailroom + +v7.1.0 +---------- + * Remove msgs_msg.response_to_id + v7.0.0 ---------- * Test on PG12 and 13 diff --git a/archives/archives_test.go b/archives/archives_test.go index 58aafaf..0fc6d01 100644 --- a/archives/archives_test.go +++ b/archives/archives_test.go @@ -143,9 +143,9 @@ func TestCreateMsgArchive(t *testing.T) { // should have two records, second will have attachments assert.Equal(t, 3, task.RecordCount) - assert.Equal(t, int64(483), task.Size) + assert.Equal(t, int64(528), task.Size) assert.Equal(t, time.Date(2017, 8, 12, 0, 0, 0, 0, time.UTC), task.StartDate) - assert.Equal(t, "6fe9265860425cf1f9757ba3d91b1a05", task.Hash) + assert.Equal(t, "b3bf00bf1234ea47f14ffd0171a8ead0", task.Hash) assertArchiveFile(t, task, "messages1.jsonl") DeleteArchiveFile(task) @@ -163,8 +163,8 @@ func TestCreateMsgArchive(t *testing.T) { // should have one record assert.Equal(t, 1, task.RecordCount) - assert.Equal(t, int64(290), task.Size) - assert.Equal(t, "a719c7ec64c516a6e159d26a70cb4225", task.Hash) + assert.Equal(t, int64(294), task.Size) + assert.Equal(t, "bd163ead077774425aa559e30d48ca87", task.Hash) assertArchiveFile(t, task, "messages2.jsonl") DeleteArchiveFile(task) @@ -218,8 +218,8 @@ func TestCreateRunArchive(t *testing.T) { // should have two record assert.Equal(t, 2, task.RecordCount) - assert.Equal(t, int64(642), task.Size) - assert.Equal(t, "f793f863f5e060b9d67c5688a555da6a", task.Hash) + assert.Equal(t, int64(472), task.Size) + assert.Equal(t, "734d437e1c66d09e033d698c732178f8", task.Hash) assertArchiveFile(t, task, "runs1.jsonl") DeleteArchiveFile(task) @@ -238,8 +238,8 @@ func TestCreateRunArchive(t *testing.T) { // should have one record assert.Equal(t, 1, task.RecordCount) - assert.Equal(t, int64(497), task.Size) - assert.Equal(t, "074de71dfb619c78dbac5b6709dd66c2", task.Hash) + assert.Equal(t, int64(490), task.Size) + assert.Equal(t, "c2138e3c3009a9c09fc55482903d93e4", task.Hash) assertArchiveFile(t, task, "runs2.jsonl") DeleteArchiveFile(task) @@ -341,14 +341,14 @@ func TestArchiveOrgMessages(t *testing.T) { assert.Equal(t, time.Date(2017, 8, 12, 0, 0, 0, 0, time.UTC), created[2].StartDate) assert.Equal(t, DayPeriod, created[2].Period) assert.Equal(t, 3, created[2].RecordCount) - assert.Equal(t, int64(483), created[2].Size) - assert.Equal(t, "6fe9265860425cf1f9757ba3d91b1a05", created[2].Hash) + assert.Equal(t, int64(528), created[2].Size) + assert.Equal(t, "b3bf00bf1234ea47f14ffd0171a8ead0", created[2].Hash) assert.Equal(t, time.Date(2017, 8, 13, 0, 0, 0, 0, time.UTC), created[3].StartDate) assert.Equal(t, DayPeriod, created[3].Period) assert.Equal(t, 1, created[3].RecordCount) - assert.Equal(t, int64(306), created[3].Size) - assert.Equal(t, "7ece4401d3afac9c08a913398f213ffa", created[3].Hash) + assert.Equal(t, int64(312), created[3].Size) + assert.Equal(t, "32e61b1431217b59fca0170f637d78a3", created[3].Hash) assert.Equal(t, time.Date(2017, 10, 10, 0, 0, 0, 0, time.UTC), created[60].StartDate) assert.Equal(t, DayPeriod, created[60].Period) @@ -359,8 +359,8 @@ func TestArchiveOrgMessages(t *testing.T) { assert.Equal(t, time.Date(2017, 8, 1, 0, 0, 0, 0, time.UTC), created[61].StartDate) assert.Equal(t, MonthPeriod, created[61].Period) assert.Equal(t, 4, created[61].RecordCount) - assert.Equal(t, int64(509), created[61].Size) - assert.Equal(t, "9e40be76913bf58655b70ee96dcac25d", created[61].Hash) + assert.Equal(t, int64(553), created[61].Size) + assert.Equal(t, "156e45e29b6587cb85ccf75e03800b00", created[61].Hash) assert.Equal(t, time.Date(2017, 9, 1, 0, 0, 0, 0, time.UTC), created[62].StartDate) assert.Equal(t, MonthPeriod, created[62].Period) @@ -469,8 +469,8 @@ func TestArchiveOrgRuns(t *testing.T) { assert.Equal(t, time.Date(2017, 8, 1, 0, 0, 0, 0, time.UTC), created[0].StartDate) assert.Equal(t, MonthPeriod, created[0].Period) assert.Equal(t, 1, created[0].RecordCount) - assert.Equal(t, int64(497), created[0].Size) - assert.Equal(t, "074de71dfb619c78dbac5b6709dd66c2", created[0].Hash) + assert.Equal(t, int64(490), created[0].Size) + assert.Equal(t, "c2138e3c3009a9c09fc55482903d93e4", created[0].Hash) assert.Equal(t, time.Date(2017, 9, 1, 0, 0, 0, 0, time.UTC), created[1].StartDate) assert.Equal(t, MonthPeriod, created[1].Period) @@ -487,8 +487,8 @@ func TestArchiveOrgRuns(t *testing.T) { assert.Equal(t, time.Date(2017, 10, 10, 0, 0, 0, 0, time.UTC), created[11].StartDate) assert.Equal(t, DayPeriod, created[11].Period) assert.Equal(t, 2, created[11].RecordCount) - assert.Equal(t, int64(2002), created[11].Size) - assert.Equal(t, "b75d6ee33ce26b786f1b341e875ecd62", created[11].Hash) + assert.Equal(t, int64(1984), created[11].Size) + assert.Equal(t, "869cc00ad4cca0371d07c88d8cf2bf26", created[11].Hash) assert.Equal(t, 12, len(deleted)) diff --git a/archives/messages.go b/archives/messages.go index a3a5119..8d44d7f 100644 --- a/archives/messages.go +++ b/archives/messages.go @@ -20,6 +20,7 @@ SELECT rec.visibility, row_to_json(rec) FROM ( row_to_json(contact) as contact, CASE WHEN oo.is_anon = False THEN ccu.identity ELSE null END as urn, row_to_json(channel) as channel, + row_to_json(flow) as flow, CASE WHEN direction = 'I' THEN 'in' WHEN direction = 'O' THEN 'out' ELSE NULL @@ -61,6 +62,7 @@ SELECT rec.visibility, row_to_json(rec) FROM ( JOIN LATERAL (select uuid, name from contacts_contact cc where cc.id = mm.contact_id) as contact ON True LEFT JOIN contacts_contacturn ccu ON mm.contact_urn_id = ccu.id LEFT JOIN LATERAL (select uuid, name from channels_channel ch where ch.id = mm.channel_id) as channel ON True + LEFT JOIN LATERAL (select uuid, name from flows_flow f where f.id = mm.flow_id) as flow ON True LEFT JOIN LATERAL (select coalesce(jsonb_agg(label_row), '[]'::jsonb) as data from (select uuid, name from msgs_label ml INNER JOIN msgs_msg_labels mml ON ml.id = mml.label_id AND mml.msg_id = mm.id) as label_row) as labels_agg ON True WHERE mm.org_id = $1 AND mm.created_on >= $2 AND mm.created_on < $3 @@ -123,12 +125,6 @@ DELETE FROM msgs_msg_labels WHERE msg_id IN(?) ` -const unlinkResponses = ` -UPDATE msgs_msg -SET response_to_id = NULL -WHERE response_to_id IN(?) -` - const deleteMessages = ` DELETE FROM msgs_msg WHERE id IN(?) @@ -189,9 +185,7 @@ func DeleteArchivedMessages(ctx context.Context, config *Config, db *sqlx.DB, s3 } rows.Close() - log.WithFields(logrus.Fields{ - "msg_count": len(msgIDs), - }).Debug("found messages") + log.WithField("msg_count", len(msgIDs)).Debug("found messages") // verify we don't see more messages than there are in our archive (fewer is ok) if visibleCount > archive.RecordCount { @@ -199,19 +193,13 @@ func DeleteArchivedMessages(ctx context.Context, config *Config, db *sqlx.DB, s3 } // ok, delete our messages in batches, we do this in transactions as it spans a few different queries - for startIdx := 0; startIdx < len(msgIDs); startIdx += deleteTransactionSize { + for _, idBatch := range chunkIDs(msgIDs, deleteTransactionSize) { // no single batch should take more than a few minutes ctx, cancel := context.WithTimeout(ctx, time.Minute*15) defer cancel() start := time.Now() - endIdx := startIdx + deleteTransactionSize - if endIdx > len(msgIDs) { - endIdx = len(msgIDs) - } - batchIDs := msgIDs[startIdx:endIdx] - // start our transaction tx, err := db.BeginTxx(ctx, nil) if err != nil { @@ -219,45 +207,36 @@ func DeleteArchivedMessages(ctx context.Context, config *Config, db *sqlx.DB, s3 } // first update our delete_reason - err = executeInQuery(ctx, tx, setMessageDeleteReason, batchIDs) + err = executeInQuery(ctx, tx, setMessageDeleteReason, idBatch) if err != nil { - return fmt.Errorf("error updating delete reason: %s", err.Error()) + return errors.Wrap(err, "error updating delete reason") } // now delete any channel logs - err = executeInQuery(ctx, tx, deleteMessageLogs, batchIDs) + err = executeInQuery(ctx, tx, deleteMessageLogs, idBatch) if err != nil { - return fmt.Errorf("error removing channel logs: %s", err.Error()) + return errors.Wrap(err, "error removing channel logs") } // then any labels - err = executeInQuery(ctx, tx, deleteMessageLabels, batchIDs) + err = executeInQuery(ctx, tx, deleteMessageLabels, idBatch) if err != nil { - return fmt.Errorf("error removing message labels: %s", err.Error()) - } - - // unlink any responses - err = executeInQuery(ctx, tx, unlinkResponses, batchIDs) - if err != nil { - return fmt.Errorf("error unlinking responses: %s", err.Error()) + return errors.Wrap(err, "error removing message labels") } // finally, delete our messages - err = executeInQuery(ctx, tx, deleteMessages, batchIDs) + err = executeInQuery(ctx, tx, deleteMessages, idBatch) if err != nil { - return fmt.Errorf("error deleting messages: %s", err.Error()) + return errors.Wrap(err, "error deleting messages") } // commit our transaction err = tx.Commit() if err != nil { - return fmt.Errorf("error committing message delete transaction: %s", err.Error()) + return errors.Wrap(err, "error committing message delete transaction") } - log.WithFields(logrus.Fields{ - "elapsed": time.Since(start), - "count": len(batchIDs), - }).Debug("deleted batch of messages") + log.WithField("elapsed", time.Since(start)).WithField("count", len(idBatch)).Debug("deleted batch of messages") cancel() } @@ -270,14 +249,12 @@ func DeleteArchivedMessages(ctx context.Context, config *Config, db *sqlx.DB, s3 // all went well! mark our archive as no longer needing deletion _, err = db.ExecContext(outer, setArchiveDeleted, archive.ID, deletedOn) if err != nil { - return fmt.Errorf("error setting archive as deleted: %s", err.Error()) + return errors.Wrap(err, "error setting archive as deleted") } archive.NeedsDeletion = false archive.DeletedOn = &deletedOn - logrus.WithFields(logrus.Fields{ - "elapsed": time.Since(start), - }).Info("completed deleting messages") + logrus.WithField("elapsed", time.Since(start)).Info("completed deleting messages") return nil } diff --git a/archives/runs.go b/archives/runs.go index c2e314e..361764d 100644 --- a/archives/runs.go +++ b/archives/runs.go @@ -12,6 +12,15 @@ import ( "github.com/sirupsen/logrus" ) +const ( + RunStatusActive = "A" + RunStatusWaiting = "W" + RunStatusCompleted = "C" + RunStatusExpired = "X" + RunStatusInterrupted = "I" + RunStatusFailed = "F" +) + const lookupFlowRuns = ` SELECT rec.exited_on, row_to_json(rec) FROM ( @@ -29,24 +38,15 @@ FROM ( SELECT key, jsonb_build_object('name', value -> 'name', 'value', value -> 'value', 'input', value -> 'input', 'time', (value -> 'created_on')::text::timestamptz, 'category', value -> 'category', 'node', value -> 'node_uuid') as value FROM jsonb_each(fr.results::jsonb)) AS values_data ) as values, - CASE - WHEN $1 - THEN '[]'::jsonb - ELSE - coalesce(fr.events, '[]'::jsonb) - END AS events, fr.created_on, fr.modified_on, fr.exited_on, CASE - WHEN exit_type = 'C' - THEN 'completed' - WHEN exit_type = 'I' - THEN 'interrupted' - WHEN exit_type = 'E' - THEN 'expired' - ELSE - null + WHEN status = 'C' THEN 'completed' + WHEN status = 'I' THEN 'interrupted' + WHEN status = 'X' THEN 'expired' + WHEN status = 'F' THEN 'failed' + ELSE NULL END as exit_type, a.username as submitted_by @@ -55,7 +55,7 @@ FROM ( JOIN LATERAL (SELECT uuid, name FROM flows_flow WHERE flows_flow.id = fr.flow_id) AS flow_struct ON True JOIN LATERAL (SELECT uuid, name FROM contacts_contact cc WHERE cc.id = fr.contact_id) AS contact_struct ON True - WHERE fr.org_id = $2 AND fr.modified_on >= $3 AND fr.modified_on < $4 + WHERE fr.org_id = $1 AND fr.modified_on >= $2 AND fr.modified_on < $3 ORDER BY fr.modified_on ASC, id ASC ) as rec; ` @@ -63,7 +63,7 @@ FROM ( // writeRunRecords writes the runs in the archive's date range to the passed in writer func writeRunRecords(ctx context.Context, db *sqlx.DB, archive *Archive, writer *bufio.Writer) (int, error) { var rows *sqlx.Rows - rows, err := db.QueryxContext(ctx, lookupFlowRuns, archive.Org.IsAnon, archive.Org.ID, archive.StartDate, archive.endDate()) + rows, err := db.QueryxContext(ctx, lookupFlowRuns, archive.Org.ID, archive.StartDate, archive.endDate()) if err != nil { return 0, errors.Wrapf(err, "error querying run records for org: %d", archive.Org.ID) } @@ -93,7 +93,7 @@ func writeRunRecords(ctx context.Context, db *sqlx.DB, archive *Archive, writer } const selectOrgRunsInRange = ` -SELECT fr.id, fr.is_active +SELECT fr.id, fr.status FROM flows_flowrun fr LEFT JOIN contacts_contact cc ON cc.id = fr.contact_id WHERE fr.org_id = $1 AND fr.modified_on >= $2 AND fr.modified_on < $3 @@ -106,17 +106,6 @@ SET delete_reason = 'A' WHERE id IN(?) ` -const deleteRecentRuns = ` -DELETE FROM flows_flowpathrecentrun -WHERE run_id IN(?) -` - -const unlinkParents = ` -UPDATE flows_flowrun -SET parent_id = NULL -WHERE parent_id IN(?) -` - const deleteRuns = ` DELETE FROM flows_flowrun WHERE id IN(?) @@ -160,18 +149,18 @@ func DeleteArchivedRuns(ctx context.Context, config *Config, db *sqlx.DB, s3Clie defer rows.Close() var runID int64 - var isActive bool + var status string runCount := 0 runIDs := make([]int64, 0, archive.RecordCount) for rows.Next() { - err = rows.Scan(&runID, &isActive) + err = rows.Scan(&runID, &status) if err != nil { return err } // if this run is still active, something has gone wrong, throw an error - if isActive { - return fmt.Errorf("run %d in archive is still active", runID) + if status == RunStatusActive || status == RunStatusWaiting { + return fmt.Errorf("run #%d in archive hadn't exited", runID) } // increment our count @@ -180,9 +169,7 @@ func DeleteArchivedRuns(ctx context.Context, config *Config, db *sqlx.DB, s3Clie } rows.Close() - log.WithFields(logrus.Fields{ - "run_count": len(runIDs), - }).Debug("found runs") + log.WithField("run_count", len(runIDs)).Debug("found runs") // verify we don't see more runs than there are in our archive (fewer is ok) if runCount > archive.RecordCount { @@ -190,19 +177,13 @@ func DeleteArchivedRuns(ctx context.Context, config *Config, db *sqlx.DB, s3Clie } // ok, delete our runs in batches, we do this in transactions as it spans a few different queries - for startIdx := 0; startIdx < len(runIDs); startIdx += deleteTransactionSize { + for _, idBatch := range chunkIDs(runIDs, deleteTransactionSize) { // no single batch should take more than a few minutes ctx, cancel := context.WithTimeout(ctx, time.Minute*15) defer cancel() start := time.Now() - endIdx := startIdx + deleteTransactionSize - if endIdx > len(runIDs) { - endIdx = len(runIDs) - } - batchIDs := runIDs[startIdx:endIdx] - // start our transaction tx, err := db.BeginTxx(ctx, nil) if err != nil { @@ -210,39 +191,24 @@ func DeleteArchivedRuns(ctx context.Context, config *Config, db *sqlx.DB, s3Clie } // first update our delete_reason - err = executeInQuery(ctx, tx, setRunDeleteReason, batchIDs) - if err != nil { - return fmt.Errorf("error updating delete reason: %s", err.Error()) - } - - // any recent runs - err = executeInQuery(ctx, tx, deleteRecentRuns, batchIDs) - if err != nil { - return fmt.Errorf("error deleting recent runs: %s", err.Error()) - } - - // unlink any parents - err = executeInQuery(ctx, tx, unlinkParents, batchIDs) + err = executeInQuery(ctx, tx, setRunDeleteReason, idBatch) if err != nil { - return fmt.Errorf("error unliking parent runs: %s", err.Error()) + return errors.Wrap(err, "error updating delete reason") } // finally, delete our runs - err = executeInQuery(ctx, tx, deleteRuns, batchIDs) + err = executeInQuery(ctx, tx, deleteRuns, idBatch) if err != nil { - return fmt.Errorf("error deleting runs: %s", err.Error()) + return errors.Wrap(err, "error deleting runs") } // commit our transaction err = tx.Commit() if err != nil { - return fmt.Errorf("error committing run delete transaction: %s", err.Error()) + return errors.Wrap(err, "error committing run delete transaction") } - log.WithFields(logrus.Fields{ - "elapsed": time.Since(start), - "count": len(batchIDs), - }).Debug("deleted batch of runs") + log.WithField("elapsed", time.Since(start)).WithField("count", len(idBatch)).Debug("deleted batch of runs") cancel() } @@ -255,14 +221,12 @@ func DeleteArchivedRuns(ctx context.Context, config *Config, db *sqlx.DB, s3Clie // all went well! mark our archive as no longer needing deletion _, err = db.ExecContext(outer, setArchiveDeleted, archive.ID, deletedOn) if err != nil { - return fmt.Errorf("error setting archive as deleted: %s", err.Error()) + return errors.Wrap(err, "error setting archive as deleted") } archive.NeedsDeletion = false archive.DeletedOn = &deletedOn - logrus.WithFields(logrus.Fields{ - "elapsed": time.Since(start), - }).Info("completed deleting runs") + logrus.WithField("elapsed", time.Since(start)).Info("completed deleting runs") return nil } diff --git a/archives/testdata/messages1.jsonl b/archives/testdata/messages1.jsonl index f1c6bda..6b53f3e 100644 --- a/archives/testdata/messages1.jsonl +++ b/archives/testdata/messages1.jsonl @@ -1,3 +1,3 @@ -{"id":1,"broadcast":null,"contact":{"uuid":"3e814add-e614-41f7-8b5d-a07f670a698f","name":"Ajodinabiff Dane"},"urn":"tel:+12067797777","channel":{"uuid":"60f2ed5b-05f2-4156-9ff0-e44e90da1b85","name":"Channel 2"},"direction":"in","type":"inbox","status":"handled","visibility":"visible","text":"message 1","attachments":[],"labels":[{"name": "Label 1", "uuid": "1d9e3188-b74b-4ae0-a166-0de31aedb34a"}, {"name": "Label 2", "uuid": "c5a69101-8dc3-444f-8b0b-5ab816e46eec"}],"created_on":"2017-08-12T21:11:59.890662+00:00","sent_on":"2017-08-12T21:11:59.890662+00:00","modified_on":"2017-08-12T21:11:59.890662+00:00"} -{"id":3,"broadcast":null,"contact":{"uuid":"3e814add-e614-41f7-8b5d-a07f670a698f","name":"Ajodinabiff Dane"},"urn":"tel:+12067797777","channel":null,"direction":"out","type":"inbox","status":"handled","visibility":"visible","text":"message 3","attachments":[{"url": "https://foo.bar/image1.png", "content_type": "image/png"}, {"url": "https://foo.bar/image2.png", "content_type": "image/png"}],"labels":[{"name": "Label 2", "uuid": "c5a69101-8dc3-444f-8b0b-5ab816e46eec"}],"created_on":"2017-08-12T21:11:59.890662+00:00","sent_on":"2017-08-12T21:11:59.890662+00:00","modified_on":"2017-08-12T21:11:59.890662+00:00"} -{"id":9,"broadcast":null,"contact":{"uuid":"3e814add-e614-41f7-8b5d-a07f670a698f","name":"Ajodinabiff Dane"},"urn":null,"channel":null,"direction":"out","type":"flow","status":"sent","visibility":"visible","text":"message 9","attachments":[],"labels":[],"created_on":"2017-08-12T21:11:59.890662+00:00","sent_on":"2017-08-12T21:11:59.890662+00:00","modified_on":"2017-08-12T21:11:59.890662+00:00"} +{"id":1,"broadcast":null,"contact":{"uuid":"3e814add-e614-41f7-8b5d-a07f670a698f","name":"Ajodinabiff Dane"},"urn":"tel:+12067797777","channel":{"uuid":"60f2ed5b-05f2-4156-9ff0-e44e90da1b85","name":"Channel 2"},"flow":null,"direction":"in","type":"inbox","status":"handled","visibility":"visible","text":"message 1","attachments":[],"labels":[{"name": "Label 1", "uuid": "1d9e3188-b74b-4ae0-a166-0de31aedb34a"}, {"name": "Label 2", "uuid": "c5a69101-8dc3-444f-8b0b-5ab816e46eec"}],"created_on":"2017-08-12T21:11:59.890662+00:00","sent_on":"2017-08-12T21:11:59.890662+00:00","modified_on":"2017-08-12T21:11:59.890662+00:00"} +{"id":3,"broadcast":null,"contact":{"uuid":"3e814add-e614-41f7-8b5d-a07f670a698f","name":"Ajodinabiff Dane"},"urn":"tel:+12067797777","channel":null,"flow":null,"direction":"out","type":"inbox","status":"handled","visibility":"visible","text":"message 3","attachments":[{"url": "https://foo.bar/image1.png", "content_type": "image/png"}, {"url": "https://foo.bar/image2.png", "content_type": "image/png"}],"labels":[{"name": "Label 2", "uuid": "c5a69101-8dc3-444f-8b0b-5ab816e46eec"}],"created_on":"2017-08-12T21:11:59.890662+00:00","sent_on":"2017-08-12T21:11:59.890662+00:00","modified_on":"2017-08-12T21:11:59.890662+00:00"} +{"id":9,"broadcast":null,"contact":{"uuid":"3e814add-e614-41f7-8b5d-a07f670a698f","name":"Ajodinabiff Dane"},"urn":null,"channel":null,"flow":{"uuid":"3914b88e-625b-4603-bd9f-9319dc331c6b","name":"Flow 3"},"direction":"out","type":"flow","status":"sent","visibility":"visible","text":"message 9","attachments":[],"labels":[],"created_on":"2017-08-12T21:11:59.890662+00:00","sent_on":"2017-08-12T21:11:59.890662+00:00","modified_on":"2017-08-12T21:11:59.890662+00:00"} diff --git a/archives/testdata/messages2.jsonl b/archives/testdata/messages2.jsonl index a01bd44..01f5bc6 100644 --- a/archives/testdata/messages2.jsonl +++ b/archives/testdata/messages2.jsonl @@ -1 +1 @@ -{"id":5,"broadcast":null,"contact":{"uuid":"7051dff0-0a27-49d7-af1f-4494239139e6","name":"Joanne Stone"},"urn":null,"channel":{"uuid":"b79e0054-068f-4928-a5f4-339d10a7ad5a","name":"Channel 3"},"direction":"in","type":"inbox","status":"handled","visibility":"visible","text":"message 5","attachments":[],"labels":[],"created_on":"2017-08-11T19:11:59.890662+00:00","sent_on":"2017-08-11T19:11:59.890662+00:00","modified_on":"2017-08-11T19:11:59.890662+00:00"} +{"id":5,"broadcast":null,"contact":{"uuid":"7051dff0-0a27-49d7-af1f-4494239139e6","name":"Joanne Stone"},"urn":null,"channel":{"uuid":"b79e0054-068f-4928-a5f4-339d10a7ad5a","name":"Channel 3"},"flow":null,"direction":"in","type":"inbox","status":"handled","visibility":"visible","text":"message 5","attachments":[],"labels":[],"created_on":"2017-08-11T19:11:59.890662+00:00","sent_on":"2017-08-11T19:11:59.890662+00:00","modified_on":"2017-08-11T19:11:59.890662+00:00"} diff --git a/archives/testdata/runs1.jsonl b/archives/testdata/runs1.jsonl index 5ca0964..3e7f8ea 100644 --- a/archives/testdata/runs1.jsonl +++ b/archives/testdata/runs1.jsonl @@ -1,2 +1,2 @@ -{"id":1,"uuid":"4ced1260-9cfe-4b7f-81dd-b637108f15b9","flow":{"uuid":"6639286a-9120-45d4-aa39-03ae3942a4a6","name":"Flow 1"},"contact":{"uuid":"3e814add-e614-41f7-8b5d-a07f670a698f","name":"Ajodinabiff Dane"},"responded":true,"path":[],"values":{},"events":[],"created_on":"2017-08-12T19:11:59.890662+00:00","modified_on":"2017-08-12T19:11:59.890662+00:00","exited_on":"2017-08-12T19:11:59.890662+00:00","exit_type":"completed","submitted_by":null} -{"id":2,"uuid":"7d68469c-0494-498a-bdf3-bac68321fd6d","flow":{"uuid":"6639286a-9120-45d4-aa39-03ae3942a4a6","name":"Flow 1"},"contact":{"uuid":"3e814add-e614-41f7-8b5d-a07f670a698f","name":"Ajodinabiff Dane"},"responded":true,"path":[{"node": "10896d63-8df7-4022-88dd-a9d93edf355b", "time": "2017-08-12T13:07:24.049815+00:00"}],"values":{"agree": {"name": "Do you agree?", "node": "a0434c54-3e26-4eb0-bafc-46cdeaf435ac", "time": "2017-05-03T12:25:21.714339+00:00", "input": "A", "value": "A", "category": "Strongly agree"}},"events":[{"msg": {"urn": "tel:+12076661212", "text": "hola", "uuid": "cf05c58f-31fb-4ce8-9e65-4ecc9fd47cbe", "channel": {"name": "1223", "uuid": "bbfe2e9c-cf69-4d0a-b42e-00ac3dc0b0b8"}}, "type": "msg_created", "step_uuid": "659cdae5-1f29-4a58-9437-10421f724268", "created_on": "2018-01-22T15:06:47.357682+00:00"}],"created_on":"2017-08-12T19:11:59.890662+00:00","modified_on":"2017-08-12T19:11:59.890662+00:00","exited_on":"2017-08-12T19:11:59.890662+00:00","exit_type":"completed","submitted_by":null} +{"id":1,"uuid":"4ced1260-9cfe-4b7f-81dd-b637108f15b9","flow":{"uuid":"6639286a-9120-45d4-aa39-03ae3942a4a6","name":"Flow 1"},"contact":{"uuid":"3e814add-e614-41f7-8b5d-a07f670a698f","name":"Ajodinabiff Dane"},"responded":true,"path":[],"values":{},"created_on":"2017-08-12T19:11:59.890662+00:00","modified_on":"2017-08-12T19:11:59.890662+00:00","exited_on":"2017-08-12T19:11:59.890662+00:00","exit_type":"completed","submitted_by":null} +{"id":2,"uuid":"7d68469c-0494-498a-bdf3-bac68321fd6d","flow":{"uuid":"6639286a-9120-45d4-aa39-03ae3942a4a6","name":"Flow 1"},"contact":{"uuid":"3e814add-e614-41f7-8b5d-a07f670a698f","name":"Ajodinabiff Dane"},"responded":true,"path":[{"node": "10896d63-8df7-4022-88dd-a9d93edf355b", "time": "2017-08-12T13:07:24.049815+00:00"}],"values":{"agree": {"name": "Do you agree?", "node": "a0434c54-3e26-4eb0-bafc-46cdeaf435ac", "time": "2017-05-03T12:25:21.714339+00:00", "input": "A", "value": "A", "category": "Strongly agree"}},"created_on":"2017-08-12T19:11:59.890662+00:00","modified_on":"2017-08-12T19:11:59.890662+00:00","exited_on":"2017-08-12T19:11:59.890662+00:00","exit_type":"completed","submitted_by":null} diff --git a/archives/testdata/runs2.jsonl b/archives/testdata/runs2.jsonl index 2875b6e..3c82b3c 100644 --- a/archives/testdata/runs2.jsonl +++ b/archives/testdata/runs2.jsonl @@ -1 +1 @@ -{"id":3,"uuid":"de782b35-a398-46ed-8550-34c66053841b","flow":{"uuid":"629db399-a5fb-4fa0-88e6-f479957b63d2","name":"Flow 2"},"contact":{"uuid":"7051dff0-0a27-49d7-af1f-4494239139e6","name":"Joanne Stone"},"responded":true,"path":[{"node": "accbc6e2-b0df-46cd-9a76-bff0fdf4d753", "time": "2017-08-12T13:07:24.049815+00:00"}],"values":{"agree": {"name": "Agree", "node": "084c8cf1-715d-4d0a-b38d-a616ed74e638", "time": "2017-05-03T12:25:21.714339+00:00", "input": "A", "value": "A", "category": "Strongly agree"}, "confirm_agree": {"name": "Do you agree?", "node": "a0434c54-3e26-4eb0-bafc-46cdeaf435ab", "time": "2017-05-03T12:25:21.714339+00:00", "input": "A", "value": "A", "category": "Confirmed Strongly agree"}},"events":[],"created_on":"2017-08-10T19:11:59.890662+00:00","modified_on":"2017-08-10T19:11:59.890662+00:00","exited_on":"2017-08-10T19:11:59.890662+00:00","exit_type":"completed","submitted_by":"greg@gmail.com"} +{"id":3,"uuid":"de782b35-a398-46ed-8550-34c66053841b","flow":{"uuid":"629db399-a5fb-4fa0-88e6-f479957b63d2","name":"Flow 2"},"contact":{"uuid":"7051dff0-0a27-49d7-af1f-4494239139e6","name":"Joanne Stone"},"responded":true,"path":[{"node": "accbc6e2-b0df-46cd-9a76-bff0fdf4d753", "time": "2017-08-12T13:07:24.049815+00:00"}],"values":{"agree": {"name": "Agree", "node": "084c8cf1-715d-4d0a-b38d-a616ed74e638", "time": "2017-05-03T12:25:21.714339+00:00", "input": "A", "value": "A", "category": "Strongly agree"}, "confirm_agree": {"name": "Do you agree?", "node": "a0434c54-3e26-4eb0-bafc-46cdeaf435ab", "time": "2017-05-03T12:25:21.714339+00:00", "input": "A", "value": "A", "category": "Confirmed Strongly agree"}},"created_on":"2017-08-10T19:11:59.890662+00:00","modified_on":"2017-08-10T19:11:59.890662+00:00","exited_on":"2017-08-10T19:11:59.890662+00:00","exit_type":"completed","submitted_by":"greg@gmail.com"} diff --git a/archives/utils.go b/archives/utils.go new file mode 100644 index 0000000..d50db23 --- /dev/null +++ b/archives/utils.go @@ -0,0 +1,15 @@ +package archives + +// chunks a slice of in64 IDs +func chunkIDs(ids []int64, size int) [][]int64 { + chunks := make([][]int64, 0, len(ids)/size+1) + + for i := 0; i < len(ids); i += size { + end := i + size + if end > len(ids) { + end = len(ids) + } + chunks = append(chunks, ids[i:end]) + } + return chunks +} diff --git a/testdb.sql b/testdb.sql index c4bdb2e..ca6868f 100644 --- a/testdb.sql +++ b/testdb.sql @@ -62,6 +62,13 @@ CREATE TABLE contacts_contactgroup_contacts ( contact_id integer NOT NULL ); +DROP TABLE IF EXISTS flows_flow CASCADE; +CREATE TABLE flows_flow ( + id serial primary key, + uuid character varying(36) NOT NULL, + name character varying(128) NOT NULL +); + DROP TABLE IF EXISTS channels_channellog CASCADE; DROP TABLE IF EXISTS msgs_msg_labels CASCADE; DROP TABLE IF EXISTS msgs_msg CASCADE; @@ -88,10 +95,10 @@ CREATE TABLE msgs_msg ( contact_id integer NOT NULL references contacts_contact(id) on delete cascade, contact_urn_id integer NULL references contacts_contacturn(id) on delete cascade, org_id integer NOT NULL references orgs_org(id) on delete cascade, + flow_id integer NULL references flows_flow(id) on delete cascade, metadata text, topup_id integer, - delete_reason char(1) NULL, - response_to_id integer NULL references msgs_msg(id) + delete_reason char(1) NULL ); DROP TABLE IF EXISTS msgs_broadcast_recipients; @@ -146,13 +153,6 @@ CREATE TABLE msgs_msg_labels ( label_id integer NOT NULL ); -DROP TABLE IF EXISTS flows_flow CASCADE; -CREATE TABLE flows_flow ( - id serial primary key, - uuid character varying(36) NOT NULL, - name character varying(128) NOT NULL -); - DROP TABLE IF EXISTS auth_user CASCADE; CREATE TABLE auth_user ( id serial primary key, @@ -160,12 +160,10 @@ CREATE TABLE auth_user ( ); DROP TABLE IF EXISTS api_webhookevent CASCADE; -DROP TABLE IF EXISTS flows_flowpathrecentrun CASCADE; DROP TABLE IF EXISTS flows_actionlog CASCADE; DROP TABLE IF EXISTS flows_flowrun CASCADE; CREATE TABLE flows_flowrun ( id serial primary key, - is_active boolean NOT NULL DEFAULT FALSE, uuid character varying(36) NOT NULL UNIQUE, responded boolean NOT NULL, contact_id integer NOT NULL references contacts_contact(id), @@ -180,7 +178,6 @@ CREATE TABLE flows_flowrun ( exited_on timestamp with time zone NULL, submitted_by_id integer NULL references auth_user(id), status varchar(1) NOT NULL, - exit_type varchar(1) NULL, delete_reason char(1) NULL ); @@ -207,11 +204,6 @@ CREATE TABLE channels_channellog ( msg_id integer NOT NULL references msgs_msg(id) ); -CREATE TABLE flows_flowpathrecentrun ( - id serial primary key, - run_id integer NOT NULL references flows_flowrun(id) DEFERRABLE INITIALLY DEFERRED -); - INSERT INTO orgs_org(id, name, is_active, is_anon, created_on) VALUES (1, 'Org 1', TRUE, FALSE, '2017-11-10 21:11:59.890662+00'), (2, 'Org 2', TRUE, FALSE, '2017-08-10 21:11:59.890662+00'), @@ -263,21 +255,27 @@ INSERT INTO contacts_contactgroup_contacts(id, contact_id, contactgroup_id) VALU (3, 1, 4), (4, 3, 4); +INSERT INTO flows_flow(id, uuid, name) VALUES +(1, '6639286a-9120-45d4-aa39-03ae3942a4a6', 'Flow 1'), +(2, '629db399-a5fb-4fa0-88e6-f479957b63d2', 'Flow 2'), +(3, '3914b88e-625b-4603-bd9f-9319dc331c6b', 'Flow 3'), +(4, 'cfa2371d-2f06-481d-84b2-d974f3803bb0', 'Flow 4'); + INSERT INTO msgs_broadcast(id, text, created_on, purged, org_id, schedule_id) VALUES (1, 'eng=>"hello",fre=>"bonjour"'::hstore, '2017-08-12 22:11:59.890662+02:00', TRUE, 2, 1), (2, 'base=>"hola"'::hstore, '2017-08-12 22:11:59.890662+02:00', TRUE, 2, NULL), (3, 'base=>"not purged"'::hstore, '2017-08-12 19:11:59.890662+02:00', FALSE, 2, NULL), (4, 'base=>"new"'::hstore, '2019-08-12 19:11:59.890662+02:00', FALSE, 2, NULL); -INSERT INTO msgs_msg(id, broadcast_id, uuid, text, created_on, sent_on, modified_on, direction, status, visibility, msg_type, attachments, channel_id, contact_id, contact_urn_id, org_id, msg_count, error_count, next_attempt, response_to_id) VALUES -(1, NULL, '2f969340-704a-4aa2-a1bd-2f832a21d257', 'message 1', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', 'I', 'H', 'V', 'I', NULL, 2, 6, 7, 2, 1, 0, '2017-08-12 21:11:59.890662+00', NULL), -(2, NULL, 'abe87ac1-015c-4803-be29-1e89509fe682', 'message 2', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', 'I', 'H', 'D', 'I', NULL, 2, 6, 7, 2, 1, 0, '2017-08-12 21:11:59.890662+00', NULL), -(3, NULL, 'a7e83a22-a6ff-4e18-82d0-19545640ccba', 'message 3', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', 'O', 'H', 'V', 'I', '{"image/png:https://foo.bar/image1.png", "image/png:https://foo.bar/image2.png"}', NULL, 6, 7, 2, 1, 0, '2017-08-12 21:11:59.890662+00', NULL), -(4, NULL, '1cad36af-5581-4c8a-81cd-83708398f61e', 'message 4', '2017-08-13 21:11:59.890662+00', '2017-08-13 21:11:59.890662+00', '2017-08-13 21:11:59.890662+00', 'I', 'H', 'V', 'I', NULL, 2, 6, 7, 2, 1, 0, '2017-08-13 21:11:59.890662+00', NULL), -(5, NULL, 'f557972e-2eb5-42fa-9b87-902116d18787', 'message 5', '2017-08-11 21:11:59.890662+02:00', '2017-08-11 21:11:59.890662+02:00', '2017-08-11 21:11:59.890662+02:00', 'I', 'H', 'V', 'I', NULL, 3, 7, 8, 3, 1, 0, '2017-08-11 21:11:59.890662+02:00', NULL), -(6, 2, '579d148c-0ab1-4afb-832f-afb1fe0e19b7', 'message 6', '2017-10-08 21:11:59.890662+00', '2017-10-08 21:11:59.890662+00', '2017-10-08 21:11:59.890662+00', 'I', 'H', 'V', 'I', NULL, 2, 6, 7, 2, 1, 0, '2017-10-08 21:11:59.890662+00', NULL), -(7, NULL, '7aeca469-2593-444e-afe4-4702317534c9', 'message 7', '2018-01-02 21:11:59.890662+00', '2018-01-02 21:11:59.890662+00', '2018-01-02 21:11:59.890662+00', 'I', 'H', 'V', 'I', NULL, 2, 6, 7, 2, 1, 0, '2018-01-02 21:11:59.890662+00', 2), -(9, NULL, 'e14ab466-0d3b-436d-a0f7-5851fd7d9b7d', 'message 9', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', 'O', 'S', 'V', 'F', NULL, NULL, 6, NULL, 2, 1, 0, '2017-08-12 21:11:59.890662+00', NULL); +INSERT INTO msgs_msg(id, broadcast_id, uuid, text, created_on, sent_on, modified_on, direction, status, visibility, msg_type, attachments, channel_id, contact_id, contact_urn_id, org_id, flow_id, msg_count, error_count, next_attempt) VALUES +(1, NULL, '2f969340-704a-4aa2-a1bd-2f832a21d257', 'message 1', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', 'I', 'H', 'V', 'I', NULL, 2, 6, 7, 2, NULL, 1, 0, '2017-08-12 21:11:59.890662+00'), +(2, NULL, 'abe87ac1-015c-4803-be29-1e89509fe682', 'message 2', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', 'I', 'H', 'D', 'I', NULL, 2, 6, 7, 2, NULL, 1, 0, '2017-08-12 21:11:59.890662+00'), +(3, NULL, 'a7e83a22-a6ff-4e18-82d0-19545640ccba', 'message 3', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', 'O', 'H', 'V', 'I', '{"image/png:https://foo.bar/image1.png", "image/png:https://foo.bar/image2.png"}', NULL, 6, 7, 2, NULL, 1, 0, '2017-08-12 21:11:59.890662+00'), +(4, NULL, '1cad36af-5581-4c8a-81cd-83708398f61e', 'message 4', '2017-08-13 21:11:59.890662+00', '2017-08-13 21:11:59.890662+00', '2017-08-13 21:11:59.890662+00', 'I', 'H', 'V', 'I', NULL, 2, 6, 7, 2, NULL, 1, 0, '2017-08-13 21:11:59.890662+00'), +(5, NULL, 'f557972e-2eb5-42fa-9b87-902116d18787', 'message 5', '2017-08-11 21:11:59.890662+02:00', '2017-08-11 21:11:59.890662+02:00', '2017-08-11 21:11:59.890662+02:00', 'I', 'H', 'V', 'I', NULL, 3, 7, 8, 3, NULL, 1, 0, '2017-08-11 21:11:59.890662+02:00'), +(6, 2, '579d148c-0ab1-4afb-832f-afb1fe0e19b7', 'message 6', '2017-10-08 21:11:59.890662+00', '2017-10-08 21:11:59.890662+00', '2017-10-08 21:11:59.890662+00', 'I', 'H', 'V', 'I', NULL, 2, 6, 7, 2, NULL, 1, 0, '2017-10-08 21:11:59.890662+00'), +(7, NULL, '7aeca469-2593-444e-afe4-4702317534c9', 'message 7', '2018-01-02 21:11:59.890662+00', '2018-01-02 21:11:59.890662+00', '2018-01-02 21:11:59.890662+00', 'I', 'H', 'V', 'F', NULL, 2, 6, 7, 2, 2, 1, 0, '2018-01-02 21:11:59.890662+00'), +(9, NULL, 'e14ab466-0d3b-436d-a0f7-5851fd7d9b7d', 'message 9', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', '2017-08-12 21:11:59.890662+00', 'O', 'S', 'V', 'F', NULL, NULL, 6, NULL, 2, 3, 1, 0, '2017-08-12 21:11:59.890662+00'); INSERT INTO msgs_label(id, uuid, name) VALUES (1, '1d9e3188-b74b-4ae0-a166-0de31aedb34a', 'Label 1'), @@ -298,37 +296,28 @@ INSERT INTO channels_channellog(id, msg_id) VALUES (5, 5), (6, 6); -INSERT INTO flows_flow(id, uuid, name) VALUES -(1, '6639286a-9120-45d4-aa39-03ae3942a4a6', 'Flow 1'), -(2, '629db399-a5fb-4fa0-88e6-f479957b63d2', 'Flow 2'), -(3, '3914b88e-625b-4603-bd9f-9319dc331c6b', 'Flow 3'), -(4, 'cfa2371d-2f06-481d-84b2-d974f3803bb0', 'Flow 4'); - INSERT INTO auth_user(id, username) VALUES (1, 'greg@gmail.com'); -INSERT INTO flows_flowrun(id, uuid, responded, contact_id, flow_id, org_id, results, path, events, created_on, modified_on, exited_on, status, exit_type, parent_id, submitted_by_id) VALUES -(1, '4ced1260-9cfe-4b7f-81dd-b637108f15b9', TRUE, 6, 1, 2, '{}', '[]', '[]', '2017-08-12 21:11:59.890662+02:00','2017-08-12 21:11:59.890662+02:00','2017-08-12 21:11:59.890662+02:00', 'C', 'C', NULL, NULL), +INSERT INTO flows_flowrun(id, uuid, responded, contact_id, flow_id, org_id, results, path, events, created_on, modified_on, exited_on, status, submitted_by_id) VALUES +(1, '4ced1260-9cfe-4b7f-81dd-b637108f15b9', TRUE, 6, 1, 2, '{}', '[]', '[]', '2017-08-12 21:11:59.890662+02:00','2017-08-12 21:11:59.890662+02:00','2017-08-12 21:11:59.890662+02:00', 'C', NULL), (2, '7d68469c-0494-498a-bdf3-bac68321fd6d', TRUE, 6, 1, 2, '{"agree": {"category": "Strongly agree", "node_uuid": "a0434c54-3e26-4eb0-bafc-46cdeaf435ac", "name": "Do you agree?", "value": "A", "created_on": "2017-05-03T12:25:21.714339+00:00", "input": "A"}}', '[{"uuid": "c3d0b417-db75-417c-8050-33776ec8f620", "node_uuid": "10896d63-8df7-4022-88dd-a9d93edf355b", "arrived_on": "2017-08-12T15:07:24.049815+02:00", "exit_uuid": "2f890507-2ad2-4bd1-92fc-0ca031155fca"}]', '[{"msg": {"urn": "tel:+12076661212", "text": "hola", "uuid": "cf05c58f-31fb-4ce8-9e65-4ecc9fd47cbe", "channel": {"name": "1223", "uuid": "bbfe2e9c-cf69-4d0a-b42e-00ac3dc0b0b8"}}, "type": "msg_created", "step_uuid": "659cdae5-1f29-4a58-9437-10421f724268", "created_on": "2018-01-22T15:06:47.357682+00:00"}]', -'2017-08-12 21:11:59.890662+02:00','2017-08-12 21:11:59.890662+02:00','2017-08-12 21:11:59.890662+02:00', 'C', 'C', NULL, NULL), +'2017-08-12 21:11:59.890662+02:00','2017-08-12 21:11:59.890662+02:00','2017-08-12 21:11:59.890662+02:00', 'C', NULL), (3, 'de782b35-a398-46ed-8550-34c66053841b', TRUE, 7, 2, 3, '{"agree": {"category": "Strongly agree", "node_uuid": "084c8cf1-715d-4d0a-b38d-a616ed74e638", "name": "Agree", "value": "A", "created_on": "2017-05-03T12:25:21.714339+00:00", "input": "A"}, "confirm_agree": {"category": "Confirmed Strongly agree", "node_uuid": "a0434c54-3e26-4eb0-bafc-46cdeaf435ab", "name": "Do you agree?", "value": "A", "created_on": "2017-05-03T12:25:21.714339+00:00", "input": "A"}}', '[{"uuid": "600ac5b4-4895-4161-ad97-6e2f1bb48bcb", "node_uuid": "accbc6e2-b0df-46cd-9a76-bff0fdf4d753", "arrived_on": "2017-08-12T15:07:24.049815+02:00", "exit_uuid": "8249e2dc-c893-4200-b6d2-398d07a459bc"}]', '[{"msg": {"urn": "tel:+12076661212", "text": "hola", "uuid": "9ea50923-0888-4596-9a9d-4890994934a9", "channel": {"name": "1223", "uuid": "d6597e08-8285-428c-8e7e-97c68adfa073"}}, "type": "msg_created", "step_uuid": "ae067248-df92-41c8-bb29-92506e984259", "created_on": "2018-01-22T15:06:47.357682+00:00"}]', -'2017-08-10 21:11:59.890662+02:00','2017-08-10 21:11:59.890662+02:00','2017-08-10 21:11:59.890662+02:00', 'C', 'C', NULL, 1), +'2017-08-10 21:11:59.890662+02:00','2017-08-10 21:11:59.890662+02:00','2017-08-10 21:11:59.890662+02:00', 'C', 1), (4, '329a5d24-64fc-479c-8d24-9674c9b46530', TRUE, 7, 2, 3, '{"agree": {"category": "Disagree", "node_uuid": "084c8cf1-715d-4d0a-b38d-a616ed74e638", "name": "Agree", "value": "B", "created_on": "2017-10-10T12:25:21.714339+00:00", "input": "B"}}', '[{"uuid": "babf4fc8-e12c-4bb9-a9dd-61178a118b5a", "node_uuid": "accbc6e2-b0df-46cd-9a76-bff0fdf4d753", "arrived_on": "2017-10-12T15:07:24.049815+02:00", "exit_uuid": "8249e2dc-c893-4200-b6d2-398d07a459bc"}]', '[{"msg": {"urn": "tel:+12076661212", "text": "hi hi", "uuid": "543d2c4b-ff0b-4b87-a9a4-b2d6745cf470", "channel": {"name": "1223", "uuid": "d6597e08-8285-428c-8e7e-97c68adfa073"}}, "type": "msg_created", "step_uuid": "3a5014dd-7b14-4b7a-be52-0419c09340a6", "created_on": "2018-10-12T15:06:47.357682+00:00"}]', -'2017-10-10 21:11:59.890662+02:00','2017-10-10 21:11:59.890662+02:00','2017-10-10 21:11:59.890662+02:00', 'C', 'C', NULL, NULL), -(5, 'abed67d2-06b8-4749-8bb9-ecda037b673b', TRUE, 7, 2, 3, '{}', '[]', '[]', '2017-10-10 21:11:59.890663+02:00','2017-10-10 21:11:59.890662+02:00','2017-10-10 21:11:59.890662+02:00', 'C', 'C', NULL, NULL), -(6, '6262eefe-a6e9-4201-9b76-a7f25e3b7f29', TRUE, 7, 2, 3, '{}', '[]', '[]', '2017-12-12 21:11:59.890662+02:00','2017-12-12 21:11:59.890662+02:00','2017-12-12 21:11:59.890662+02:00', 'C', 'C', 4, NULL); - -INSERT INTO flows_flowpathrecentrun(id, run_id) VALUES -(1, 3); +'2017-10-10 21:11:59.890662+02:00','2017-10-10 21:11:59.890662+02:00','2017-10-10 21:11:59.890662+02:00', 'C', NULL), +(5, 'abed67d2-06b8-4749-8bb9-ecda037b673b', TRUE, 7, 2, 3, '{}', '[]', '[]', '2017-10-10 21:11:59.890663+02:00','2017-10-10 21:11:59.890662+02:00','2017-10-10 21:11:59.890662+02:00', 'C', NULL), +(6, '6262eefe-a6e9-4201-9b76-a7f25e3b7f29', TRUE, 7, 2, 3, '{}', '[]', '[]', '2017-12-12 21:11:59.890662+02:00','2017-12-12 21:11:59.890662+02:00','2017-12-12 21:11:59.890662+02:00', 'C', NULL); -- update run #5 to have a path longer than 500 steps UPDATE flows_flowrun SET path = s.path FROM (