From 5afe783edcfaeba9fc2ff8df35de40fc0112539a Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Fri, 10 Jan 2025 11:30:58 +0000 Subject: [PATCH 01/44] wip: replace knex for new database migrations --- lib/bin/run-migrations.js | 7 ++++--- lib/model/migrate.js | 36 ++++++++++++++++++++++++++++++++++-- lib/task/task.js | 3 ++- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/lib/bin/run-migrations.js b/lib/bin/run-migrations.js index 0a681ce1b..9549c3cf2 100644 --- a/lib/bin/run-migrations.js +++ b/lib/bin/run-migrations.js @@ -7,13 +7,14 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { withDatabase, migrate } = require('../model/migrate'); +const { withDatabase, knexMigrations, postKnexMigrations } = require('../model/migrate'); (async () => { try { - await withDatabase(require('config').get('default.database'))(migrate); + await withKnex(require('config').get('default.database'))(knexMigrations); + await withSlonikOrPg(postKnexMigrations); } catch (err) { - console.error('Error:', err.message); + console.error(err); process.exit(1); } })(); diff --git a/lib/model/migrate.js b/lib/model/migrate.js index fe18ed2f5..cf4da24c6 100644 --- a/lib/model/migrate.js +++ b/lib/model/migrate.js @@ -11,6 +11,7 @@ // top-level operations with a database, like migrations. const knex = require('knex'); +const { Migrator } = require('knex/lib/migrate/Migrator'); const { connectionObject } = require('../util/db'); // Connects to the postgres database specified in configuration and returns it. @@ -23,7 +24,38 @@ const withDatabase = (config) => (mutator) => { }; // Given a database, initiates migrations on it. -const migrate = (db) => db.migrate.latest({ directory: `${__dirname}/migrations` }); +const knexMigrations = (db) => db.migrate.latest({ directory: `${__dirname}/migrations/legacy` }); +const postKnexMigrations = (db) => { + // In the main, this migrator is written to behave similarly to knex's: + // + // * re-uses knex_migrations table + // * expects transaction property async .up({ raw }) + // * provides implementation of db.raw() + // + // Notable differences + // + // * ONLY provides db.raw() function to transactions - no knex query builder etc. + // * ONLY implements up(); will throw if a transaction has other properties, except for `down()` which will be ignored for pre-2025 migrations + // * does not use a separate knex_migrations_lock table (in fact, this should be DROPped as the first post-knex migration TODO is this comfortable?) + // * gets list of migrations to run _after_ acquiring db lock + // * sets migration_time to be the start of the migration batch's transaction rather than some other intermediate time + + + // TODO consider whether the knex_migrations_lock table is useful vs. just locking the whole migrations table (probably fine) + // TODO can we just fail if there is ANY waiting for lock acquisition? probably simpler than handling edge cases like knex does (probably) + // TODO would life be simpler if we get the list of migrations to run AFTER acquiring the lock? (probably) + // TODO understand whether we want to run all migrations in the same transaction, or separately. seems like currently all run in the same transaction, although knex provides options for isolating a single migration, and for using separate transactions for each + + // 1. TODO start transaction (most aggressive type) + // 2. TODO if migration table does not exist, CREATE IT + // 3. TODO get lock on knex_migrations table; throw if lock not available IMMEDIATELY + // 4. TODO get list of migrations to run + // 5. TODO validate migrations to run (e.g. do they have unexpected properties) + // 7. TODO remove any migrations from list which were run while waiting for the lock + // 8. TODO run all migrations + // 9. TODO get migration batch number (COALESCE(MAX(batch), 0) + 1) + // 10. TODO update knex_migrations table to include newly-run migrations (migration_time should either be NOW() or CLOCK_TIMESTAMP(), but currently unclear whether knex has been running all migrations in the same tx or not) +}; // Checks for pending migrations and returns an exit code of 1 if any are // still pending/unapplied (e.g. automatically running migrations just failed). @@ -33,5 +65,5 @@ const checkMigrations = (db) => db.migrate.list({ directory: `${__dirname}/migra process.exitCode = 1; }); -module.exports = { checkMigrations, connect, withDatabase, migrate }; +module.exports = { checkMigrations, connect, withDatabase, knexMigrations, postKnexMigrations }; diff --git a/lib/task/task.js b/lib/task/task.js index 516e6c29d..f72fa23e2 100644 --- a/lib/task/task.js +++ b/lib/task/task.js @@ -75,6 +75,7 @@ const writeTo = (output) => (x) => output.write(`${x}\n`); const writeToStderr = writeTo(process.stderr); /* istanbul ignore next */ const fault = (error) => { + console.log('fault()', error); // first print our error. if ((error != null) && (error.isProblem === true) && (error.httpCode < 500)) { writeToStderr(error.message); @@ -105,7 +106,7 @@ const auditing = (action, t) => ((typeof t === 'function') return Promise.resolve(result); }) )), - ((error) => auditLog(action, false, Option.of(error).map(Problem.serializable).orNull()).then( + ((error) => console.log('auditing() error', error) || auditLog(action, false, Option.of(error).map(Problem.serializable).orNull()).then( (() => Promise.reject(error)), ((auditError) => { writeToStderr('Failed to audit-log task failure message!'); From 7a0260d54ac332009c08f18aeb1ed84b98a9f564 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 09:39:56 +0000 Subject: [PATCH 02/44] move all the migrations; implement the migrator --- lib/bin/check-migrations.js | 4 +- lib/bin/run-migrations.js | 12 +- lib/model/knexfile.js | 4 +- lib/model/migrate.js | 165 +++++++++++++++--- ...-01-disable-nullable-blob-content-types.js | 23 +++ .../{ => legacy}/20170920-01-initial.js | 0 .../{ => legacy}/20171010-01-auth.js | 0 .../{ => legacy}/20171023-01-authz-forms.js | 0 .../20171030-01-add-default-authz-records.js | 0 ...0171106-01-remove-user-update-timestamp.js | 0 .../20171121-01-add-submissions-constraint.js | 0 .../{ => legacy}/20171121-02-add-submitter.js | 0 .../20171213-01-unrequire-display-name.js | 0 .../20180108-01-expiring-actors.js | 0 .../20180108-02-enum-to-varchar.js | 0 .../{ => legacy}/20180112-01-audit-table.js | 0 .../20180112-02-add-field-keys.js | 0 .../20180118-01-rerequire-display-name.js | 0 .../20180125-01-add-form-detail-fields.js | 0 .../20180125-02-more-field-key-grants.js | 0 .../20180125-03-add-blob-tables.js | 0 .../{ => legacy}/20180301-01-configuration.js | 0 .../20180322-01-additional-form-options.js | 0 .../20180327-01-update-form-constraints.js | 0 .../20180501-01-add-configs-timestamp.js | 0 .../20180501-02-fix-date-columns.js | 0 ...0180515-01-enforce-nonnull-form-version.js | 0 .../20180727-01-rename-attachments-table.js | 0 .../20180727-02-add-md5-to-blobs.js | 0 .../20180727-03-add-form-attachments-table.js | 0 .../20181011-make-email-case-insensitive.js | 0 ...1012-01-add-submissions-createdat-index.js | 0 .../{ => legacy}/20181206-01-add-projects.js | 0 .../20181207-01-grant-verbs-to-text.js | 0 .../20181207-02-rename-grant-verbs.js | 0 .../20181211-01-audit-verbs-to-text.js | 0 .../20181211-02-rename-audit-actions.js | 0 .../{ => legacy}/20181212-00-fix-user-type.js | 0 .../{ => legacy}/20181212-01-add-roles.js | 0 .../{ => legacy}/20181212-02-remove-groups.js | 0 .../20181212-03-add-single-use-roles.js | 0 .../20181219-01-add-submission-update-verb.js | 0 .../20181221-01-nullable-submission-blobs.js | 0 ...20181230-01-add-device-id-to-submission.js | 0 .../20190225-01-add-actor-trigram-indices.js | 0 .../20190225-02-add-role-grants.js | 0 .../20190226-01-convert-verbs-to-jsonb.js | 0 .../20190226-02-add-role-actee-species.js | 0 .../20190226-03-add-assignment-verbs.js | 0 ...0190226-04-add-assignment-actee-species.js | 0 .../20190227-01-add-project-manager-role.js | 0 .../20190405-01-add-project-archival-flag.js | 0 .../20190416-01-email-uniqueness.js | 0 .../20190416-02-add-user-delete-verb.js | 0 .../20190520-01-add-form-versioning.js | 0 .../20190523-01-add-form-state-constraint.js | 0 .../20190605-01-reformat-audits.js | 0 ...90607-01-convert-audit-details-to-jsonb.js | 0 ...190607-02-standardize-attachment-actees.js | 0 ...0190607-03-rename-sub-attachment-audits.js | 0 .../20190610-01-add-audits-verbs.js | 0 ...2-backfill-submission-audit-instanceids.js | 0 ...11-01-add-updatedat-to-form-attachments.js | 0 .../20190618-01-add-csrf-token.js | 0 .../20190618-01-add-encryption-tracking.js | 0 ...701-01-add-managed-encryption-key-check.js | 0 ...916-01-granularize-app-user-permissions.js | 0 .../20190917-01-cleanup-app-user-role.js | 0 .../20190923-01-add-project-viewer-role.js | 0 .../20190925-01-add-client-audits.js | 0 .../20191007-01-backfill-client-audits.js | 0 .../20191010-01-add-excel-blob-reference.js | 0 ...0191023-01-add-worker-columns-to-audits.js | 0 .../20191025-01-add-id-to-audits.js | 0 ...106-01-remove-deleted-actor-assignments.js | 0 .../20191231-01-remove-transformations.js | 0 .../20191231-02-add-schema-storage.js | 0 .../{ => legacy}/20200110-01-add-drafts.js | 0 .../20200112-01-check-field-collisions.js | 0 ...0114-01-remove-formid-sha256-constraint.js | 0 .../20200117-01-draft-test-submissions.js | 0 .../20200121-01-add-draft-keys.js | 0 .../20200122-01-remove-draft-form-state.js | 0 .../20200129-01-cascade-submission-deletes.js | 0 .../20200220-01-repair-submission-parsing.js | 0 .../20200403-01-add-performance-indices.js | 0 ...0407-01-allow-actorless-submission-defs.js | 0 ...0200423-01-fix-field-insert-performance.js | 0 .../20200428-01-allow-string-downcast.js | 0 .../{ => legacy}/20200519-01-add-enketo-id.js | 0 .../20200519-02-add-form-viewer-role.js | 0 .../20200520-01-backfill-enketo.js | 0 .../20200715-01-add-data-collector-role.js | 0 .../20200721-01-add-public-links.js | 0 ...728-01-add-enketo-single-token-to-forms.js | 0 ...-allow-project-managers-to-end-sessions.js | 0 ...0200810-01-reschedule-enketo-processing.js | 0 .../20200918-01-repair-publishedat-dates.js | 0 .../20200930-01-add-backup-run-verb.js | 0 ...-remove-deleted-actor-assignments-again.js | 0 ...01207-01-harmonize-submitter-id-columns.js | 0 ...-01-add-current-flag-to-submission-defs.js | 0 .../20210120-01-instance-names.js | 0 .../20210203-01-add-hierarchy-to-actees.js | 0 ...10-01-add-instanceid-to-submission-defs.js | 0 .../20210218-01-add-submission-edit-verbs.js | 0 ...0218-02-add-draft-to-submissions-unique.js | 0 .../20210219-01-add-review-state.js | 0 ...210219-02-add-notes-and-index-to-audits.js | 0 ...1-add-submission-edit-verbs-to-managers.js | 0 .../20210325-01-remove-project.list-verb.js | 0 .../20210408-01-drop-public-link-createdat.js | 0 ...08-02-backfill-specialized-actor-audits.js | 0 .../{ => legacy}/20210409-01-add-comments.js | 0 .../20210409-02-update-review-states.js | 0 .../20210423-01-add-name-to-form-def.js | 0 .../20210423-02-drop-form-name.js | 0 .../20210716-01-config-value-jsonb.js | 0 .../20210721-01-add-config-set-verb.js | 0 ...1-disallow-structure-downcast-to-string.js | 0 .../20210825-01-add-analytics-read-verb.js | 0 ...903-01-backfill-encrypted-client-audits.js | 0 ...7-01-revert-disallow-structure-downcast.js | 0 .../20211008-01-track-select-many-options.js | 0 .../20211021-remove-hashes-from-audits.js | 0 ...211109-01-add-user-agent-to-submissions.js | 0 ...20211114-01-flag-initial-submission-def.js | 0 .../20211117-01-add-form-restore-verb.js | 0 ...0211129-01-add-purged-details-to-actees.js | 0 .../20220121-01-form-cascade-delete.js | 0 .../20220121-02-purge-deleted-forms.js | 0 .../20220209-01-purge-unneeded-drafts.js | 0 .../20220309-01-add-project-description.js | 0 .../20220803-01-create-entities-schema.js | 0 .../20221003-01-add-dataset-verbs.js | 0 .../20221114-01-explict-dataset-publish.js | 0 ...eck-datasetId-is-null-for-non-file-type.js | 0 ...21118-01-make-entities-columns-not-null.js | 0 .../20221208-01-reduce-tz-precision.js | 0 .../20230106-01-remove-revision-number.js | 0 .../20230109-01-add-form-schema.js | 0 .../20230123-01-remove-google-backups.js | 0 .../20230126-01-add-entity-indices.js | 0 .../20230127-01-rename-entity-created-by.js | 0 .../20230324-01-edit-dataset-verbs.js | 0 .../20230406-01-add-entity-def-fields.js | 0 ...0406-02-move-entity-label-add-deletedAt.js | 0 .../20230414-01-remove-user-mfa-secret.js | 0 .../20230419-01-optimize-indices-sub-defs.js | 0 .../20230509-01-dataset-approval-flag.js | 0 .../20230512-01-add-entity-root.js | 0 .../20230512-02-backfill-entity-id.js | 0 .../20230512-03-add-entity-source.js | 0 .../20230518-01-add-entity-index-to-audits.js | 0 .../20230802-01-delete-orphan-submissions.js | 0 ...1-remove-schemaId-from-dsPropertyFields.js | 0 .../20230824-01-add-entity-version.js | 0 ...0830-01-remove-entity-label-from-audits.js | 0 .../20230907-01-opened-form-verb.js | 0 .../20231002-01-add-conflict-details.js | 0 .../20231013-01-change-entity-error-action.js | 0 .../20231208-01-dataset-form-def-actions.js | 0 .../20240215-01-entity-delete-verb.js | 0 .../{ => legacy}/20240215-02-dedupe-verbs.js | 0 .../20240312-01-add-dataset-create-verb.js | 0 ...22-01-add-entity-source-index-to-audits.js | 0 .../20240515-01-entity-tz-precision.js | 0 ...01-add-offline-entity-branch-trunk-info.js | 0 .../20240607-02-add-submission-backlog.js | 0 ...0240715-01-backlog-add-event-entityuuid.js | 0 .../{ => legacy}/20240913-01-add-blob-s3.js | 0 .../20240914-01-add-submission-delete-verb.js | 0 ...240914-02-remove-orphaned-client-audits.js | 0 .../20241001-01-index-on-session-table.js | 0 .../20241008-01-add-user_preferences.js | 0 ...0241010-01-schedule-entity-form-upgrade.js | 0 ...hedule-entity-form-upgrade-create-forms.js | 0 ...20241030-01-add-force-entity-def-source.js | 0 lib/util/db.js | 4 +- test/unit/util/db.js | 18 +- 180 files changed, 188 insertions(+), 42 deletions(-) create mode 100644 lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js rename lib/model/migrations/{ => legacy}/20170920-01-initial.js (100%) rename lib/model/migrations/{ => legacy}/20171010-01-auth.js (100%) rename lib/model/migrations/{ => legacy}/20171023-01-authz-forms.js (100%) rename lib/model/migrations/{ => legacy}/20171030-01-add-default-authz-records.js (100%) rename lib/model/migrations/{ => legacy}/20171106-01-remove-user-update-timestamp.js (100%) rename lib/model/migrations/{ => legacy}/20171121-01-add-submissions-constraint.js (100%) rename lib/model/migrations/{ => legacy}/20171121-02-add-submitter.js (100%) rename lib/model/migrations/{ => legacy}/20171213-01-unrequire-display-name.js (100%) rename lib/model/migrations/{ => legacy}/20180108-01-expiring-actors.js (100%) rename lib/model/migrations/{ => legacy}/20180108-02-enum-to-varchar.js (100%) rename lib/model/migrations/{ => legacy}/20180112-01-audit-table.js (100%) rename lib/model/migrations/{ => legacy}/20180112-02-add-field-keys.js (100%) rename lib/model/migrations/{ => legacy}/20180118-01-rerequire-display-name.js (100%) rename lib/model/migrations/{ => legacy}/20180125-01-add-form-detail-fields.js (100%) rename lib/model/migrations/{ => legacy}/20180125-02-more-field-key-grants.js (100%) rename lib/model/migrations/{ => legacy}/20180125-03-add-blob-tables.js (100%) rename lib/model/migrations/{ => legacy}/20180301-01-configuration.js (100%) rename lib/model/migrations/{ => legacy}/20180322-01-additional-form-options.js (100%) rename lib/model/migrations/{ => legacy}/20180327-01-update-form-constraints.js (100%) rename lib/model/migrations/{ => legacy}/20180501-01-add-configs-timestamp.js (100%) rename lib/model/migrations/{ => legacy}/20180501-02-fix-date-columns.js (100%) rename lib/model/migrations/{ => legacy}/20180515-01-enforce-nonnull-form-version.js (100%) rename lib/model/migrations/{ => legacy}/20180727-01-rename-attachments-table.js (100%) rename lib/model/migrations/{ => legacy}/20180727-02-add-md5-to-blobs.js (100%) rename lib/model/migrations/{ => legacy}/20180727-03-add-form-attachments-table.js (100%) rename lib/model/migrations/{ => legacy}/20181011-make-email-case-insensitive.js (100%) rename lib/model/migrations/{ => legacy}/20181012-01-add-submissions-createdat-index.js (100%) rename lib/model/migrations/{ => legacy}/20181206-01-add-projects.js (100%) rename lib/model/migrations/{ => legacy}/20181207-01-grant-verbs-to-text.js (100%) rename lib/model/migrations/{ => legacy}/20181207-02-rename-grant-verbs.js (100%) rename lib/model/migrations/{ => legacy}/20181211-01-audit-verbs-to-text.js (100%) rename lib/model/migrations/{ => legacy}/20181211-02-rename-audit-actions.js (100%) rename lib/model/migrations/{ => legacy}/20181212-00-fix-user-type.js (100%) rename lib/model/migrations/{ => legacy}/20181212-01-add-roles.js (100%) rename lib/model/migrations/{ => legacy}/20181212-02-remove-groups.js (100%) rename lib/model/migrations/{ => legacy}/20181212-03-add-single-use-roles.js (100%) rename lib/model/migrations/{ => legacy}/20181219-01-add-submission-update-verb.js (100%) rename lib/model/migrations/{ => legacy}/20181221-01-nullable-submission-blobs.js (100%) rename lib/model/migrations/{ => legacy}/20181230-01-add-device-id-to-submission.js (100%) rename lib/model/migrations/{ => legacy}/20190225-01-add-actor-trigram-indices.js (100%) rename lib/model/migrations/{ => legacy}/20190225-02-add-role-grants.js (100%) rename lib/model/migrations/{ => legacy}/20190226-01-convert-verbs-to-jsonb.js (100%) rename lib/model/migrations/{ => legacy}/20190226-02-add-role-actee-species.js (100%) rename lib/model/migrations/{ => legacy}/20190226-03-add-assignment-verbs.js (100%) rename lib/model/migrations/{ => legacy}/20190226-04-add-assignment-actee-species.js (100%) rename lib/model/migrations/{ => legacy}/20190227-01-add-project-manager-role.js (100%) rename lib/model/migrations/{ => legacy}/20190405-01-add-project-archival-flag.js (100%) rename lib/model/migrations/{ => legacy}/20190416-01-email-uniqueness.js (100%) rename lib/model/migrations/{ => legacy}/20190416-02-add-user-delete-verb.js (100%) rename lib/model/migrations/{ => legacy}/20190520-01-add-form-versioning.js (100%) rename lib/model/migrations/{ => legacy}/20190523-01-add-form-state-constraint.js (100%) rename lib/model/migrations/{ => legacy}/20190605-01-reformat-audits.js (100%) rename lib/model/migrations/{ => legacy}/20190607-01-convert-audit-details-to-jsonb.js (100%) rename lib/model/migrations/{ => legacy}/20190607-02-standardize-attachment-actees.js (100%) rename lib/model/migrations/{ => legacy}/20190607-03-rename-sub-attachment-audits.js (100%) rename lib/model/migrations/{ => legacy}/20190610-01-add-audits-verbs.js (100%) rename lib/model/migrations/{ => legacy}/20190610-02-backfill-submission-audit-instanceids.js (100%) rename lib/model/migrations/{ => legacy}/20190611-01-add-updatedat-to-form-attachments.js (100%) rename lib/model/migrations/{ => legacy}/20190618-01-add-csrf-token.js (100%) rename lib/model/migrations/{ => legacy}/20190618-01-add-encryption-tracking.js (100%) rename lib/model/migrations/{ => legacy}/20190701-01-add-managed-encryption-key-check.js (100%) rename lib/model/migrations/{ => legacy}/20190916-01-granularize-app-user-permissions.js (100%) rename lib/model/migrations/{ => legacy}/20190917-01-cleanup-app-user-role.js (100%) rename lib/model/migrations/{ => legacy}/20190923-01-add-project-viewer-role.js (100%) rename lib/model/migrations/{ => legacy}/20190925-01-add-client-audits.js (100%) rename lib/model/migrations/{ => legacy}/20191007-01-backfill-client-audits.js (100%) rename lib/model/migrations/{ => legacy}/20191010-01-add-excel-blob-reference.js (100%) rename lib/model/migrations/{ => legacy}/20191023-01-add-worker-columns-to-audits.js (100%) rename lib/model/migrations/{ => legacy}/20191025-01-add-id-to-audits.js (100%) rename lib/model/migrations/{ => legacy}/20191106-01-remove-deleted-actor-assignments.js (100%) rename lib/model/migrations/{ => legacy}/20191231-01-remove-transformations.js (100%) rename lib/model/migrations/{ => legacy}/20191231-02-add-schema-storage.js (100%) rename lib/model/migrations/{ => legacy}/20200110-01-add-drafts.js (100%) rename lib/model/migrations/{ => legacy}/20200112-01-check-field-collisions.js (100%) rename lib/model/migrations/{ => legacy}/20200114-01-remove-formid-sha256-constraint.js (100%) rename lib/model/migrations/{ => legacy}/20200117-01-draft-test-submissions.js (100%) rename lib/model/migrations/{ => legacy}/20200121-01-add-draft-keys.js (100%) rename lib/model/migrations/{ => legacy}/20200122-01-remove-draft-form-state.js (100%) rename lib/model/migrations/{ => legacy}/20200129-01-cascade-submission-deletes.js (100%) rename lib/model/migrations/{ => legacy}/20200220-01-repair-submission-parsing.js (100%) rename lib/model/migrations/{ => legacy}/20200403-01-add-performance-indices.js (100%) rename lib/model/migrations/{ => legacy}/20200407-01-allow-actorless-submission-defs.js (100%) rename lib/model/migrations/{ => legacy}/20200423-01-fix-field-insert-performance.js (100%) rename lib/model/migrations/{ => legacy}/20200428-01-allow-string-downcast.js (100%) rename lib/model/migrations/{ => legacy}/20200519-01-add-enketo-id.js (100%) rename lib/model/migrations/{ => legacy}/20200519-02-add-form-viewer-role.js (100%) rename lib/model/migrations/{ => legacy}/20200520-01-backfill-enketo.js (100%) rename lib/model/migrations/{ => legacy}/20200715-01-add-data-collector-role.js (100%) rename lib/model/migrations/{ => legacy}/20200721-01-add-public-links.js (100%) rename lib/model/migrations/{ => legacy}/20200728-01-add-enketo-single-token-to-forms.js (100%) rename lib/model/migrations/{ => legacy}/20200731-01-allow-project-managers-to-end-sessions.js (100%) rename lib/model/migrations/{ => legacy}/20200810-01-reschedule-enketo-processing.js (100%) rename lib/model/migrations/{ => legacy}/20200918-01-repair-publishedat-dates.js (100%) rename lib/model/migrations/{ => legacy}/20200930-01-add-backup-run-verb.js (100%) rename lib/model/migrations/{ => legacy}/20201117-01-remove-deleted-actor-assignments-again.js (100%) rename lib/model/migrations/{ => legacy}/20201207-01-harmonize-submitter-id-columns.js (100%) rename lib/model/migrations/{ => legacy}/20210118-01-add-current-flag-to-submission-defs.js (100%) rename lib/model/migrations/{ => legacy}/20210120-01-instance-names.js (100%) rename lib/model/migrations/{ => legacy}/20210203-01-add-hierarchy-to-actees.js (100%) rename lib/model/migrations/{ => legacy}/20210210-01-add-instanceid-to-submission-defs.js (100%) rename lib/model/migrations/{ => legacy}/20210218-01-add-submission-edit-verbs.js (100%) rename lib/model/migrations/{ => legacy}/20210218-02-add-draft-to-submissions-unique.js (100%) rename lib/model/migrations/{ => legacy}/20210219-01-add-review-state.js (100%) rename lib/model/migrations/{ => legacy}/20210219-02-add-notes-and-index-to-audits.js (100%) rename lib/model/migrations/{ => legacy}/20210324-01-add-submission-edit-verbs-to-managers.js (100%) rename lib/model/migrations/{ => legacy}/20210325-01-remove-project.list-verb.js (100%) rename lib/model/migrations/{ => legacy}/20210408-01-drop-public-link-createdat.js (100%) rename lib/model/migrations/{ => legacy}/20210408-02-backfill-specialized-actor-audits.js (100%) rename lib/model/migrations/{ => legacy}/20210409-01-add-comments.js (100%) rename lib/model/migrations/{ => legacy}/20210409-02-update-review-states.js (100%) rename lib/model/migrations/{ => legacy}/20210423-01-add-name-to-form-def.js (100%) rename lib/model/migrations/{ => legacy}/20210423-02-drop-form-name.js (100%) rename lib/model/migrations/{ => legacy}/20210716-01-config-value-jsonb.js (100%) rename lib/model/migrations/{ => legacy}/20210721-01-add-config-set-verb.js (100%) rename lib/model/migrations/{ => legacy}/20210817-01-disallow-structure-downcast-to-string.js (100%) rename lib/model/migrations/{ => legacy}/20210825-01-add-analytics-read-verb.js (100%) rename lib/model/migrations/{ => legacy}/20210903-01-backfill-encrypted-client-audits.js (100%) rename lib/model/migrations/{ => legacy}/20210927-01-revert-disallow-structure-downcast.js (100%) rename lib/model/migrations/{ => legacy}/20211008-01-track-select-many-options.js (100%) rename lib/model/migrations/{ => legacy}/20211021-remove-hashes-from-audits.js (100%) rename lib/model/migrations/{ => legacy}/20211109-01-add-user-agent-to-submissions.js (100%) rename lib/model/migrations/{ => legacy}/20211114-01-flag-initial-submission-def.js (100%) rename lib/model/migrations/{ => legacy}/20211117-01-add-form-restore-verb.js (100%) rename lib/model/migrations/{ => legacy}/20211129-01-add-purged-details-to-actees.js (100%) rename lib/model/migrations/{ => legacy}/20220121-01-form-cascade-delete.js (100%) rename lib/model/migrations/{ => legacy}/20220121-02-purge-deleted-forms.js (100%) rename lib/model/migrations/{ => legacy}/20220209-01-purge-unneeded-drafts.js (100%) rename lib/model/migrations/{ => legacy}/20220309-01-add-project-description.js (100%) rename lib/model/migrations/{ => legacy}/20220803-01-create-entities-schema.js (100%) rename lib/model/migrations/{ => legacy}/20221003-01-add-dataset-verbs.js (100%) rename lib/model/migrations/{ => legacy}/20221114-01-explict-dataset-publish.js (100%) rename lib/model/migrations/{ => legacy}/20221117-01-check-datasetId-is-null-for-non-file-type.js (100%) rename lib/model/migrations/{ => legacy}/20221118-01-make-entities-columns-not-null.js (100%) rename lib/model/migrations/{ => legacy}/20221208-01-reduce-tz-precision.js (100%) rename lib/model/migrations/{ => legacy}/20230106-01-remove-revision-number.js (100%) rename lib/model/migrations/{ => legacy}/20230109-01-add-form-schema.js (100%) rename lib/model/migrations/{ => legacy}/20230123-01-remove-google-backups.js (100%) rename lib/model/migrations/{ => legacy}/20230126-01-add-entity-indices.js (100%) rename lib/model/migrations/{ => legacy}/20230127-01-rename-entity-created-by.js (100%) rename lib/model/migrations/{ => legacy}/20230324-01-edit-dataset-verbs.js (100%) rename lib/model/migrations/{ => legacy}/20230406-01-add-entity-def-fields.js (100%) rename lib/model/migrations/{ => legacy}/20230406-02-move-entity-label-add-deletedAt.js (100%) rename lib/model/migrations/{ => legacy}/20230414-01-remove-user-mfa-secret.js (100%) rename lib/model/migrations/{ => legacy}/20230419-01-optimize-indices-sub-defs.js (100%) rename lib/model/migrations/{ => legacy}/20230509-01-dataset-approval-flag.js (100%) rename lib/model/migrations/{ => legacy}/20230512-01-add-entity-root.js (100%) rename lib/model/migrations/{ => legacy}/20230512-02-backfill-entity-id.js (100%) rename lib/model/migrations/{ => legacy}/20230512-03-add-entity-source.js (100%) rename lib/model/migrations/{ => legacy}/20230518-01-add-entity-index-to-audits.js (100%) rename lib/model/migrations/{ => legacy}/20230802-01-delete-orphan-submissions.js (100%) rename lib/model/migrations/{ => legacy}/20230818-01-remove-schemaId-from-dsPropertyFields.js (100%) rename lib/model/migrations/{ => legacy}/20230824-01-add-entity-version.js (100%) rename lib/model/migrations/{ => legacy}/20230830-01-remove-entity-label-from-audits.js (100%) rename lib/model/migrations/{ => legacy}/20230907-01-opened-form-verb.js (100%) rename lib/model/migrations/{ => legacy}/20231002-01-add-conflict-details.js (100%) rename lib/model/migrations/{ => legacy}/20231013-01-change-entity-error-action.js (100%) rename lib/model/migrations/{ => legacy}/20231208-01-dataset-form-def-actions.js (100%) rename lib/model/migrations/{ => legacy}/20240215-01-entity-delete-verb.js (100%) rename lib/model/migrations/{ => legacy}/20240215-02-dedupe-verbs.js (100%) rename lib/model/migrations/{ => legacy}/20240312-01-add-dataset-create-verb.js (100%) rename lib/model/migrations/{ => legacy}/20240322-01-add-entity-source-index-to-audits.js (100%) rename lib/model/migrations/{ => legacy}/20240515-01-entity-tz-precision.js (100%) rename lib/model/migrations/{ => legacy}/20240607-01-add-offline-entity-branch-trunk-info.js (100%) rename lib/model/migrations/{ => legacy}/20240607-02-add-submission-backlog.js (100%) rename lib/model/migrations/{ => legacy}/20240715-01-backlog-add-event-entityuuid.js (100%) rename lib/model/migrations/{ => legacy}/20240913-01-add-blob-s3.js (100%) rename lib/model/migrations/{ => legacy}/20240914-01-add-submission-delete-verb.js (100%) rename lib/model/migrations/{ => legacy}/20240914-02-remove-orphaned-client-audits.js (100%) rename lib/model/migrations/{ => legacy}/20241001-01-index-on-session-table.js (100%) rename lib/model/migrations/{ => legacy}/20241008-01-add-user_preferences.js (100%) rename lib/model/migrations/{ => legacy}/20241010-01-schedule-entity-form-upgrade.js (100%) rename lib/model/migrations/{ => legacy}/20241029-01-schedule-entity-form-upgrade-create-forms.js (100%) rename lib/model/migrations/{ => legacy}/20241030-01-add-force-entity-def-source.js (100%) diff --git a/lib/bin/check-migrations.js b/lib/bin/check-migrations.js index e5d4308da..20b9c5e82 100644 --- a/lib/bin/check-migrations.js +++ b/lib/bin/check-migrations.js @@ -7,11 +7,11 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { withDatabase, checkMigrations } = require('../model/migrate'); +const { withKnex, checkMigrations } = require('../model/migrate'); (async () => { try { - await withDatabase(require('config').get('default.database'))(checkMigrations); + await withKnex(require('config').get('default.database'))(checkMigrations); } catch (err) { console.error('Error:', err.message); process.exit(1); diff --git a/lib/bin/run-migrations.js b/lib/bin/run-migrations.js index 9549c3cf2..93324f8c6 100644 --- a/lib/bin/run-migrations.js +++ b/lib/bin/run-migrations.js @@ -7,12 +7,18 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { withDatabase, knexMigrations, postKnexMigrations } = require('../model/migrate'); +const { + withKnex, + knexMigrations, + + postKnexMigrations, +} = require('../model/migrate'); (async () => { try { - await withKnex(require('config').get('default.database'))(knexMigrations); - await withSlonikOrPg(postKnexMigrations); + const config = require('config').get('default.database'); + await withKnex(config)(knexMigrations); + await postKnexMigrations(config); } catch (err) { console.error(err); process.exit(1); diff --git a/lib/model/knexfile.js b/lib/model/knexfile.js index 84622389b..511584825 100644 --- a/lib/model/knexfile.js +++ b/lib/model/knexfile.js @@ -28,10 +28,10 @@ NODE_CONFIG_DIR=../../config DEBUG=knex:query,knex:bindings npx knex migrate:up */ const config = require('config'); -const { connectionObject } = require('../util/db'); +const { knexConfig } = require('../util/db'); module.exports = { client: 'pg', - connection: connectionObject(config.get('default.database')) + connection: knexConfig(config.get('default.database')) }; diff --git a/lib/model/migrate.js b/lib/model/migrate.js index cf4da24c6..53c3b9ec7 100644 --- a/lib/model/migrate.js +++ b/lib/model/migrate.js @@ -10,51 +10,168 @@ // This is a variety of functions helpful for connecting to and performing // top-level operations with a database, like migrations. +const { lstatSync, readdirSync } = require('node:fs'); + +const _ = require('lodash'); const knex = require('knex'); +const pg = require('pg'); const { Migrator } = require('knex/lib/migrate/Migrator'); -const { connectionObject } = require('../util/db'); +const { knexConfig } = require('../util/db'); // Connects to the postgres database specified in configuration and returns it. -const connect = (config) => knex({ client: 'pg', connection: connectionObject(config) }); +const initKnex = (config) => knex({ client: 'pg', connection: knexConfig(config) }); // Connects to a database, passes it to a function for operations, then ensures its closure. -const withDatabase = (config) => (mutator) => { - const db = connect(config); +const withKnex = (config) => (mutator) => { + const db = initKnex(config); return mutator(db).finally(() => db.destroy()); }; // Given a database, initiates migrations on it. const knexMigrations = (db) => db.migrate.latest({ directory: `${__dirname}/migrations/legacy` }); -const postKnexMigrations = (db) => { + +const postKnexMigrations = async (config) => { + const log = (...args) => console.log('[postKnexMigrations]', ...args); + log('ENTRY'); + + const { Client } = pg; + const client = new Client(config); + + log('client created'); + // In the main, this migrator is written to behave similarly to knex's: // - // * re-uses knex_migrations table // * expects transaction property async .up({ raw }) // * provides implementation of db.raw() + // * runs all new migrations in the same transaction // // Notable differences // - // * ONLY provides db.raw() function to transactions - no knex query builder etc. - // * ONLY implements up(); will throw if a transaction has other properties, except for `down()` which will be ignored for pre-2025 migrations - // * does not use a separate knex_migrations_lock table (in fact, this should be DROPped as the first post-knex migration TODO is this comfortable?) + // * uses new post_knex_migrations table + // * ONLY provides db.raw()-equivalent function to transactions - no knex query builder etc. + // * ONLY implements up(); will throw if a transaction has other properties, except for `down()` which is currently ignored TODO implement this if it's useful to devs // * gets list of migrations to run _after_ acquiring db lock // * sets migration_time to be the start of the migration batch's transaction rather than some other intermediate time + try { + log('Connecting to client...'); + await client.connect(); + log('Client connected OK.'); + + log('Testing client...'); + const res = await client.query('SELECT NOW()'); + log('Client returned:', res); + + log('Starting transaction...'); + await client.query('BEGIN'); // TODO do we need a specific transaction type? + log('Transaction started.'); + + log('Acquiring knex lock...'); + // TODO do this... if it's useful. Need to think of _some_ way to prevent new migrations and old migrations running simultaneously. + log('Knex lock acquired'); + + log('Creating new table if not exists...'); + // N.B. this table is created to be similar to the legacy knex-created table. + // The key difference is that name, batch and migration_time columns are + // not nullable. + await client.query(` + CREATE TABLE IF NOT EXISTS post_knex_migrations ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + batch INTEGER NOT NULL, + migration_time TIMESTAMP(3) WITH TIME ZONE NOT NULL + )`); + log('Table now definitely exists.'); + + log('Acquiring lock on post_knex_migrations table...'); + await client.query('LOCK TABLE post_knex_migrations IN EXCLUSIVE MODE NOWAIT'); + log('Lock acquired.'); + + const migrationsDir = `${__dirname}/migrations`; + const allMigrations = readdirSync(migrationsDir) + .filter(f => f.endsWith('.js') && lstatSync(`${migrationsDir}/${f}`).isFile()) + .sort(); + log('allMigrations:', allMigrations); + + const alreadyRun = (await client.query('SELECT name FROM post_knex_migrations')).rows.map(r => r.name); + log('alreadyRun:', alreadyRun); + + const toRunNames = allMigrations.filter(m => !alreadyRun.includes(m)); + log('toRunNames:', toRunNames); + + const toRun = toRunNames.map(name => { + const path = `${migrationsDir}/${name}`; + const migration = require(path); + return { name, path, migration }; + }); + log('toRun:', toRun); + + if(!toRun.length) { + log('No migrations to run - exiting.'); + await client.query('ROLLBACK'); + return; + } + + log('Validating', toRun.length, 'migrations...'); + for(const { migration, name, path } of toRun) { + log('Validing migration:', name, '...'); + + if(name.length > 255) throw new Error(`Migration name '${name}' is too long - max length is ${maxLen}, but got ${name.length}`); + + // TODO check for illegal chars in name? + + const keys = Object.keys(migration); + const unexpectedKeys = _.omit(keys, 'up', 'down'); + if(unexpectedKeys.length) throw new Error(`Unexpected key(s) found in migration ${name}: ${unexpectedKeys}`); + + if(!migration.up) throw new Error(`Required prop .up not found in migration ${name}`); + if(typeof migration.up !== 'function') { + throw new Error(`Required prop .up of migration ${name} has incorrect type - expected 'function', but got '${typeof migration.up}'`); + } + + if(migration.down && typeof migration.down !== 'function') { + throw new Error(`Optional prop .down of migration ${name} has incorrect type - expected 'function' but got '${typeof migration.down}'`); + } + + log('Migration', name, 'looks valid.'); + } + log(toRun.length, 'migrations look valid.'); + + log('Running', toRun.length, 'migrations...'); + for (const { migration, name, path } of toRun) { + log('Running migration:', name); + await migration.up(client); + log('Migration complete:', name); + } + log(toRun.length, 'migrations ran OK.'); + + const { lastBatch } = (await client.query(`SELECT COALESCE(MAX(batch), 0) AS "lastBatch" FROM post_knex_migrations`)).rows[0]; + log('lastBatch:', lastBatch); + + // Note that migration_time is CLOCK_TIMESTAMP() to match knex implementation. + // TODO confirm in relevant version of knex source code that this is actually the case, and link here. + const namesJson = JSON.stringify(toRun.map(m => m.name)); + // See: https://www.postgresql.org/docs/current/functions-json.html + await client.query(` + INSERT INTO post_knex_migrations(name, batch, migration_time) + SELECT value#>>'{}' AS name + , ${lastBatch + 1} AS batch + , CLOCK_TIMESTAMP() AS migration_time + FROM JSON_ARRAY_ELEMENTS($1) + `, [ namesJson ]); - // TODO consider whether the knex_migrations_lock table is useful vs. just locking the whole migrations table (probably fine) - // TODO can we just fail if there is ANY waiting for lock acquisition? probably simpler than handling edge cases like knex does (probably) - // TODO would life be simpler if we get the list of migrations to run AFTER acquiring the lock? (probably) - // TODO understand whether we want to run all migrations in the same transaction, or separately. seems like currently all run in the same transaction, although knex provides options for isolating a single migration, and for using separate transactions for each - - // 1. TODO start transaction (most aggressive type) - // 2. TODO if migration table does not exist, CREATE IT - // 3. TODO get lock on knex_migrations table; throw if lock not available IMMEDIATELY - // 4. TODO get list of migrations to run - // 5. TODO validate migrations to run (e.g. do they have unexpected properties) - // 7. TODO remove any migrations from list which were run while waiting for the lock - // 8. TODO run all migrations - // 9. TODO get migration batch number (COALESCE(MAX(batch), 0) + 1) - // 10. TODO update knex_migrations table to include newly-run migrations (migration_time should either be NOW() or CLOCK_TIMESTAMP(), but currently unclear whether knex has been running all migrations in the same tx or not) + log('Committing migrations...'); + await client.query('COMMIT'); + log('Migrations committed.'); + } catch (err) { + log('Caught error; rolling back', err); + await client.query('ROLLBACK'); + throw err; + } finally { + log('Ending client...'); + await client.end(); // TODO needs await or callback? + log('Client ended.'); + } }; // Checks for pending migrations and returns an exit code of 1 if any are @@ -65,5 +182,5 @@ const checkMigrations = (db) => db.migrate.list({ directory: `${__dirname}/migra process.exitCode = 1; }); -module.exports = { checkMigrations, connect, withDatabase, knexMigrations, postKnexMigrations }; +module.exports = { checkMigrations, initKnex, withKnex, knexMigrations, postKnexMigrations }; diff --git a/lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js b/lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js new file mode 100644 index 000000000..eb7bc2c39 --- /dev/null +++ b/lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js @@ -0,0 +1,23 @@ +// Copyright 2025 ODK Central Developers +// See the NOTICE file at the top-level directory of this distribution and at +// https://github.com/getodk/central-backend/blob/master/NOTICE. +// This file is part of ODK Central. It is subject to the license terms in +// the LICENSE file found in the top-level directory of this distribution and at +// https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central, +// including this file, may be copied, modified, propagated, or distributed +// except according to the terms contained in the LICENSE file. + +const up = (db) => db.query(` + ALTER TABLE blobs + ALTER COLUMN "contentType" TYPE TEXT USING(COALESCE("contentType", 'application/octet-stream')), + ALTER COLUMN "contentType" SET DEFAULT 'application/octet-stream', + ALTER COLUMN "contentType" SET NOT NULL +`); + +const down = (db) => db.query(` + ALTER TABLE blobs + ALTER COLUMN "contentType" DROP NOT NULL, + ALTER COLUMN "contentType" DROP DEFAULT +`); + +module.exports = { up, down }; diff --git a/lib/model/migrations/20170920-01-initial.js b/lib/model/migrations/legacy/20170920-01-initial.js similarity index 100% rename from lib/model/migrations/20170920-01-initial.js rename to lib/model/migrations/legacy/20170920-01-initial.js diff --git a/lib/model/migrations/20171010-01-auth.js b/lib/model/migrations/legacy/20171010-01-auth.js similarity index 100% rename from lib/model/migrations/20171010-01-auth.js rename to lib/model/migrations/legacy/20171010-01-auth.js diff --git a/lib/model/migrations/20171023-01-authz-forms.js b/lib/model/migrations/legacy/20171023-01-authz-forms.js similarity index 100% rename from lib/model/migrations/20171023-01-authz-forms.js rename to lib/model/migrations/legacy/20171023-01-authz-forms.js diff --git a/lib/model/migrations/20171030-01-add-default-authz-records.js b/lib/model/migrations/legacy/20171030-01-add-default-authz-records.js similarity index 100% rename from lib/model/migrations/20171030-01-add-default-authz-records.js rename to lib/model/migrations/legacy/20171030-01-add-default-authz-records.js diff --git a/lib/model/migrations/20171106-01-remove-user-update-timestamp.js b/lib/model/migrations/legacy/20171106-01-remove-user-update-timestamp.js similarity index 100% rename from lib/model/migrations/20171106-01-remove-user-update-timestamp.js rename to lib/model/migrations/legacy/20171106-01-remove-user-update-timestamp.js diff --git a/lib/model/migrations/20171121-01-add-submissions-constraint.js b/lib/model/migrations/legacy/20171121-01-add-submissions-constraint.js similarity index 100% rename from lib/model/migrations/20171121-01-add-submissions-constraint.js rename to lib/model/migrations/legacy/20171121-01-add-submissions-constraint.js diff --git a/lib/model/migrations/20171121-02-add-submitter.js b/lib/model/migrations/legacy/20171121-02-add-submitter.js similarity index 100% rename from lib/model/migrations/20171121-02-add-submitter.js rename to lib/model/migrations/legacy/20171121-02-add-submitter.js diff --git a/lib/model/migrations/20171213-01-unrequire-display-name.js b/lib/model/migrations/legacy/20171213-01-unrequire-display-name.js similarity index 100% rename from lib/model/migrations/20171213-01-unrequire-display-name.js rename to lib/model/migrations/legacy/20171213-01-unrequire-display-name.js diff --git a/lib/model/migrations/20180108-01-expiring-actors.js b/lib/model/migrations/legacy/20180108-01-expiring-actors.js similarity index 100% rename from lib/model/migrations/20180108-01-expiring-actors.js rename to lib/model/migrations/legacy/20180108-01-expiring-actors.js diff --git a/lib/model/migrations/20180108-02-enum-to-varchar.js b/lib/model/migrations/legacy/20180108-02-enum-to-varchar.js similarity index 100% rename from lib/model/migrations/20180108-02-enum-to-varchar.js rename to lib/model/migrations/legacy/20180108-02-enum-to-varchar.js diff --git a/lib/model/migrations/20180112-01-audit-table.js b/lib/model/migrations/legacy/20180112-01-audit-table.js similarity index 100% rename from lib/model/migrations/20180112-01-audit-table.js rename to lib/model/migrations/legacy/20180112-01-audit-table.js diff --git a/lib/model/migrations/20180112-02-add-field-keys.js b/lib/model/migrations/legacy/20180112-02-add-field-keys.js similarity index 100% rename from lib/model/migrations/20180112-02-add-field-keys.js rename to lib/model/migrations/legacy/20180112-02-add-field-keys.js diff --git a/lib/model/migrations/20180118-01-rerequire-display-name.js b/lib/model/migrations/legacy/20180118-01-rerequire-display-name.js similarity index 100% rename from lib/model/migrations/20180118-01-rerequire-display-name.js rename to lib/model/migrations/legacy/20180118-01-rerequire-display-name.js diff --git a/lib/model/migrations/20180125-01-add-form-detail-fields.js b/lib/model/migrations/legacy/20180125-01-add-form-detail-fields.js similarity index 100% rename from lib/model/migrations/20180125-01-add-form-detail-fields.js rename to lib/model/migrations/legacy/20180125-01-add-form-detail-fields.js diff --git a/lib/model/migrations/20180125-02-more-field-key-grants.js b/lib/model/migrations/legacy/20180125-02-more-field-key-grants.js similarity index 100% rename from lib/model/migrations/20180125-02-more-field-key-grants.js rename to lib/model/migrations/legacy/20180125-02-more-field-key-grants.js diff --git a/lib/model/migrations/20180125-03-add-blob-tables.js b/lib/model/migrations/legacy/20180125-03-add-blob-tables.js similarity index 100% rename from lib/model/migrations/20180125-03-add-blob-tables.js rename to lib/model/migrations/legacy/20180125-03-add-blob-tables.js diff --git a/lib/model/migrations/20180301-01-configuration.js b/lib/model/migrations/legacy/20180301-01-configuration.js similarity index 100% rename from lib/model/migrations/20180301-01-configuration.js rename to lib/model/migrations/legacy/20180301-01-configuration.js diff --git a/lib/model/migrations/20180322-01-additional-form-options.js b/lib/model/migrations/legacy/20180322-01-additional-form-options.js similarity index 100% rename from lib/model/migrations/20180322-01-additional-form-options.js rename to lib/model/migrations/legacy/20180322-01-additional-form-options.js diff --git a/lib/model/migrations/20180327-01-update-form-constraints.js b/lib/model/migrations/legacy/20180327-01-update-form-constraints.js similarity index 100% rename from lib/model/migrations/20180327-01-update-form-constraints.js rename to lib/model/migrations/legacy/20180327-01-update-form-constraints.js diff --git a/lib/model/migrations/20180501-01-add-configs-timestamp.js b/lib/model/migrations/legacy/20180501-01-add-configs-timestamp.js similarity index 100% rename from lib/model/migrations/20180501-01-add-configs-timestamp.js rename to lib/model/migrations/legacy/20180501-01-add-configs-timestamp.js diff --git a/lib/model/migrations/20180501-02-fix-date-columns.js b/lib/model/migrations/legacy/20180501-02-fix-date-columns.js similarity index 100% rename from lib/model/migrations/20180501-02-fix-date-columns.js rename to lib/model/migrations/legacy/20180501-02-fix-date-columns.js diff --git a/lib/model/migrations/20180515-01-enforce-nonnull-form-version.js b/lib/model/migrations/legacy/20180515-01-enforce-nonnull-form-version.js similarity index 100% rename from lib/model/migrations/20180515-01-enforce-nonnull-form-version.js rename to lib/model/migrations/legacy/20180515-01-enforce-nonnull-form-version.js diff --git a/lib/model/migrations/20180727-01-rename-attachments-table.js b/lib/model/migrations/legacy/20180727-01-rename-attachments-table.js similarity index 100% rename from lib/model/migrations/20180727-01-rename-attachments-table.js rename to lib/model/migrations/legacy/20180727-01-rename-attachments-table.js diff --git a/lib/model/migrations/20180727-02-add-md5-to-blobs.js b/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js similarity index 100% rename from lib/model/migrations/20180727-02-add-md5-to-blobs.js rename to lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js diff --git a/lib/model/migrations/20180727-03-add-form-attachments-table.js b/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js similarity index 100% rename from lib/model/migrations/20180727-03-add-form-attachments-table.js rename to lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js diff --git a/lib/model/migrations/20181011-make-email-case-insensitive.js b/lib/model/migrations/legacy/20181011-make-email-case-insensitive.js similarity index 100% rename from lib/model/migrations/20181011-make-email-case-insensitive.js rename to lib/model/migrations/legacy/20181011-make-email-case-insensitive.js diff --git a/lib/model/migrations/20181012-01-add-submissions-createdat-index.js b/lib/model/migrations/legacy/20181012-01-add-submissions-createdat-index.js similarity index 100% rename from lib/model/migrations/20181012-01-add-submissions-createdat-index.js rename to lib/model/migrations/legacy/20181012-01-add-submissions-createdat-index.js diff --git a/lib/model/migrations/20181206-01-add-projects.js b/lib/model/migrations/legacy/20181206-01-add-projects.js similarity index 100% rename from lib/model/migrations/20181206-01-add-projects.js rename to lib/model/migrations/legacy/20181206-01-add-projects.js diff --git a/lib/model/migrations/20181207-01-grant-verbs-to-text.js b/lib/model/migrations/legacy/20181207-01-grant-verbs-to-text.js similarity index 100% rename from lib/model/migrations/20181207-01-grant-verbs-to-text.js rename to lib/model/migrations/legacy/20181207-01-grant-verbs-to-text.js diff --git a/lib/model/migrations/20181207-02-rename-grant-verbs.js b/lib/model/migrations/legacy/20181207-02-rename-grant-verbs.js similarity index 100% rename from lib/model/migrations/20181207-02-rename-grant-verbs.js rename to lib/model/migrations/legacy/20181207-02-rename-grant-verbs.js diff --git a/lib/model/migrations/20181211-01-audit-verbs-to-text.js b/lib/model/migrations/legacy/20181211-01-audit-verbs-to-text.js similarity index 100% rename from lib/model/migrations/20181211-01-audit-verbs-to-text.js rename to lib/model/migrations/legacy/20181211-01-audit-verbs-to-text.js diff --git a/lib/model/migrations/20181211-02-rename-audit-actions.js b/lib/model/migrations/legacy/20181211-02-rename-audit-actions.js similarity index 100% rename from lib/model/migrations/20181211-02-rename-audit-actions.js rename to lib/model/migrations/legacy/20181211-02-rename-audit-actions.js diff --git a/lib/model/migrations/20181212-00-fix-user-type.js b/lib/model/migrations/legacy/20181212-00-fix-user-type.js similarity index 100% rename from lib/model/migrations/20181212-00-fix-user-type.js rename to lib/model/migrations/legacy/20181212-00-fix-user-type.js diff --git a/lib/model/migrations/20181212-01-add-roles.js b/lib/model/migrations/legacy/20181212-01-add-roles.js similarity index 100% rename from lib/model/migrations/20181212-01-add-roles.js rename to lib/model/migrations/legacy/20181212-01-add-roles.js diff --git a/lib/model/migrations/20181212-02-remove-groups.js b/lib/model/migrations/legacy/20181212-02-remove-groups.js similarity index 100% rename from lib/model/migrations/20181212-02-remove-groups.js rename to lib/model/migrations/legacy/20181212-02-remove-groups.js diff --git a/lib/model/migrations/20181212-03-add-single-use-roles.js b/lib/model/migrations/legacy/20181212-03-add-single-use-roles.js similarity index 100% rename from lib/model/migrations/20181212-03-add-single-use-roles.js rename to lib/model/migrations/legacy/20181212-03-add-single-use-roles.js diff --git a/lib/model/migrations/20181219-01-add-submission-update-verb.js b/lib/model/migrations/legacy/20181219-01-add-submission-update-verb.js similarity index 100% rename from lib/model/migrations/20181219-01-add-submission-update-verb.js rename to lib/model/migrations/legacy/20181219-01-add-submission-update-verb.js diff --git a/lib/model/migrations/20181221-01-nullable-submission-blobs.js b/lib/model/migrations/legacy/20181221-01-nullable-submission-blobs.js similarity index 100% rename from lib/model/migrations/20181221-01-nullable-submission-blobs.js rename to lib/model/migrations/legacy/20181221-01-nullable-submission-blobs.js diff --git a/lib/model/migrations/20181230-01-add-device-id-to-submission.js b/lib/model/migrations/legacy/20181230-01-add-device-id-to-submission.js similarity index 100% rename from lib/model/migrations/20181230-01-add-device-id-to-submission.js rename to lib/model/migrations/legacy/20181230-01-add-device-id-to-submission.js diff --git a/lib/model/migrations/20190225-01-add-actor-trigram-indices.js b/lib/model/migrations/legacy/20190225-01-add-actor-trigram-indices.js similarity index 100% rename from lib/model/migrations/20190225-01-add-actor-trigram-indices.js rename to lib/model/migrations/legacy/20190225-01-add-actor-trigram-indices.js diff --git a/lib/model/migrations/20190225-02-add-role-grants.js b/lib/model/migrations/legacy/20190225-02-add-role-grants.js similarity index 100% rename from lib/model/migrations/20190225-02-add-role-grants.js rename to lib/model/migrations/legacy/20190225-02-add-role-grants.js diff --git a/lib/model/migrations/20190226-01-convert-verbs-to-jsonb.js b/lib/model/migrations/legacy/20190226-01-convert-verbs-to-jsonb.js similarity index 100% rename from lib/model/migrations/20190226-01-convert-verbs-to-jsonb.js rename to lib/model/migrations/legacy/20190226-01-convert-verbs-to-jsonb.js diff --git a/lib/model/migrations/20190226-02-add-role-actee-species.js b/lib/model/migrations/legacy/20190226-02-add-role-actee-species.js similarity index 100% rename from lib/model/migrations/20190226-02-add-role-actee-species.js rename to lib/model/migrations/legacy/20190226-02-add-role-actee-species.js diff --git a/lib/model/migrations/20190226-03-add-assignment-verbs.js b/lib/model/migrations/legacy/20190226-03-add-assignment-verbs.js similarity index 100% rename from lib/model/migrations/20190226-03-add-assignment-verbs.js rename to lib/model/migrations/legacy/20190226-03-add-assignment-verbs.js diff --git a/lib/model/migrations/20190226-04-add-assignment-actee-species.js b/lib/model/migrations/legacy/20190226-04-add-assignment-actee-species.js similarity index 100% rename from lib/model/migrations/20190226-04-add-assignment-actee-species.js rename to lib/model/migrations/legacy/20190226-04-add-assignment-actee-species.js diff --git a/lib/model/migrations/20190227-01-add-project-manager-role.js b/lib/model/migrations/legacy/20190227-01-add-project-manager-role.js similarity index 100% rename from lib/model/migrations/20190227-01-add-project-manager-role.js rename to lib/model/migrations/legacy/20190227-01-add-project-manager-role.js diff --git a/lib/model/migrations/20190405-01-add-project-archival-flag.js b/lib/model/migrations/legacy/20190405-01-add-project-archival-flag.js similarity index 100% rename from lib/model/migrations/20190405-01-add-project-archival-flag.js rename to lib/model/migrations/legacy/20190405-01-add-project-archival-flag.js diff --git a/lib/model/migrations/20190416-01-email-uniqueness.js b/lib/model/migrations/legacy/20190416-01-email-uniqueness.js similarity index 100% rename from lib/model/migrations/20190416-01-email-uniqueness.js rename to lib/model/migrations/legacy/20190416-01-email-uniqueness.js diff --git a/lib/model/migrations/20190416-02-add-user-delete-verb.js b/lib/model/migrations/legacy/20190416-02-add-user-delete-verb.js similarity index 100% rename from lib/model/migrations/20190416-02-add-user-delete-verb.js rename to lib/model/migrations/legacy/20190416-02-add-user-delete-verb.js diff --git a/lib/model/migrations/20190520-01-add-form-versioning.js b/lib/model/migrations/legacy/20190520-01-add-form-versioning.js similarity index 100% rename from lib/model/migrations/20190520-01-add-form-versioning.js rename to lib/model/migrations/legacy/20190520-01-add-form-versioning.js diff --git a/lib/model/migrations/20190523-01-add-form-state-constraint.js b/lib/model/migrations/legacy/20190523-01-add-form-state-constraint.js similarity index 100% rename from lib/model/migrations/20190523-01-add-form-state-constraint.js rename to lib/model/migrations/legacy/20190523-01-add-form-state-constraint.js diff --git a/lib/model/migrations/20190605-01-reformat-audits.js b/lib/model/migrations/legacy/20190605-01-reformat-audits.js similarity index 100% rename from lib/model/migrations/20190605-01-reformat-audits.js rename to lib/model/migrations/legacy/20190605-01-reformat-audits.js diff --git a/lib/model/migrations/20190607-01-convert-audit-details-to-jsonb.js b/lib/model/migrations/legacy/20190607-01-convert-audit-details-to-jsonb.js similarity index 100% rename from lib/model/migrations/20190607-01-convert-audit-details-to-jsonb.js rename to lib/model/migrations/legacy/20190607-01-convert-audit-details-to-jsonb.js diff --git a/lib/model/migrations/20190607-02-standardize-attachment-actees.js b/lib/model/migrations/legacy/20190607-02-standardize-attachment-actees.js similarity index 100% rename from lib/model/migrations/20190607-02-standardize-attachment-actees.js rename to lib/model/migrations/legacy/20190607-02-standardize-attachment-actees.js diff --git a/lib/model/migrations/20190607-03-rename-sub-attachment-audits.js b/lib/model/migrations/legacy/20190607-03-rename-sub-attachment-audits.js similarity index 100% rename from lib/model/migrations/20190607-03-rename-sub-attachment-audits.js rename to lib/model/migrations/legacy/20190607-03-rename-sub-attachment-audits.js diff --git a/lib/model/migrations/20190610-01-add-audits-verbs.js b/lib/model/migrations/legacy/20190610-01-add-audits-verbs.js similarity index 100% rename from lib/model/migrations/20190610-01-add-audits-verbs.js rename to lib/model/migrations/legacy/20190610-01-add-audits-verbs.js diff --git a/lib/model/migrations/20190610-02-backfill-submission-audit-instanceids.js b/lib/model/migrations/legacy/20190610-02-backfill-submission-audit-instanceids.js similarity index 100% rename from lib/model/migrations/20190610-02-backfill-submission-audit-instanceids.js rename to lib/model/migrations/legacy/20190610-02-backfill-submission-audit-instanceids.js diff --git a/lib/model/migrations/20190611-01-add-updatedat-to-form-attachments.js b/lib/model/migrations/legacy/20190611-01-add-updatedat-to-form-attachments.js similarity index 100% rename from lib/model/migrations/20190611-01-add-updatedat-to-form-attachments.js rename to lib/model/migrations/legacy/20190611-01-add-updatedat-to-form-attachments.js diff --git a/lib/model/migrations/20190618-01-add-csrf-token.js b/lib/model/migrations/legacy/20190618-01-add-csrf-token.js similarity index 100% rename from lib/model/migrations/20190618-01-add-csrf-token.js rename to lib/model/migrations/legacy/20190618-01-add-csrf-token.js diff --git a/lib/model/migrations/20190618-01-add-encryption-tracking.js b/lib/model/migrations/legacy/20190618-01-add-encryption-tracking.js similarity index 100% rename from lib/model/migrations/20190618-01-add-encryption-tracking.js rename to lib/model/migrations/legacy/20190618-01-add-encryption-tracking.js diff --git a/lib/model/migrations/20190701-01-add-managed-encryption-key-check.js b/lib/model/migrations/legacy/20190701-01-add-managed-encryption-key-check.js similarity index 100% rename from lib/model/migrations/20190701-01-add-managed-encryption-key-check.js rename to lib/model/migrations/legacy/20190701-01-add-managed-encryption-key-check.js diff --git a/lib/model/migrations/20190916-01-granularize-app-user-permissions.js b/lib/model/migrations/legacy/20190916-01-granularize-app-user-permissions.js similarity index 100% rename from lib/model/migrations/20190916-01-granularize-app-user-permissions.js rename to lib/model/migrations/legacy/20190916-01-granularize-app-user-permissions.js diff --git a/lib/model/migrations/20190917-01-cleanup-app-user-role.js b/lib/model/migrations/legacy/20190917-01-cleanup-app-user-role.js similarity index 100% rename from lib/model/migrations/20190917-01-cleanup-app-user-role.js rename to lib/model/migrations/legacy/20190917-01-cleanup-app-user-role.js diff --git a/lib/model/migrations/20190923-01-add-project-viewer-role.js b/lib/model/migrations/legacy/20190923-01-add-project-viewer-role.js similarity index 100% rename from lib/model/migrations/20190923-01-add-project-viewer-role.js rename to lib/model/migrations/legacy/20190923-01-add-project-viewer-role.js diff --git a/lib/model/migrations/20190925-01-add-client-audits.js b/lib/model/migrations/legacy/20190925-01-add-client-audits.js similarity index 100% rename from lib/model/migrations/20190925-01-add-client-audits.js rename to lib/model/migrations/legacy/20190925-01-add-client-audits.js diff --git a/lib/model/migrations/20191007-01-backfill-client-audits.js b/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js similarity index 100% rename from lib/model/migrations/20191007-01-backfill-client-audits.js rename to lib/model/migrations/legacy/20191007-01-backfill-client-audits.js diff --git a/lib/model/migrations/20191010-01-add-excel-blob-reference.js b/lib/model/migrations/legacy/20191010-01-add-excel-blob-reference.js similarity index 100% rename from lib/model/migrations/20191010-01-add-excel-blob-reference.js rename to lib/model/migrations/legacy/20191010-01-add-excel-blob-reference.js diff --git a/lib/model/migrations/20191023-01-add-worker-columns-to-audits.js b/lib/model/migrations/legacy/20191023-01-add-worker-columns-to-audits.js similarity index 100% rename from lib/model/migrations/20191023-01-add-worker-columns-to-audits.js rename to lib/model/migrations/legacy/20191023-01-add-worker-columns-to-audits.js diff --git a/lib/model/migrations/20191025-01-add-id-to-audits.js b/lib/model/migrations/legacy/20191025-01-add-id-to-audits.js similarity index 100% rename from lib/model/migrations/20191025-01-add-id-to-audits.js rename to lib/model/migrations/legacy/20191025-01-add-id-to-audits.js diff --git a/lib/model/migrations/20191106-01-remove-deleted-actor-assignments.js b/lib/model/migrations/legacy/20191106-01-remove-deleted-actor-assignments.js similarity index 100% rename from lib/model/migrations/20191106-01-remove-deleted-actor-assignments.js rename to lib/model/migrations/legacy/20191106-01-remove-deleted-actor-assignments.js diff --git a/lib/model/migrations/20191231-01-remove-transformations.js b/lib/model/migrations/legacy/20191231-01-remove-transformations.js similarity index 100% rename from lib/model/migrations/20191231-01-remove-transformations.js rename to lib/model/migrations/legacy/20191231-01-remove-transformations.js diff --git a/lib/model/migrations/20191231-02-add-schema-storage.js b/lib/model/migrations/legacy/20191231-02-add-schema-storage.js similarity index 100% rename from lib/model/migrations/20191231-02-add-schema-storage.js rename to lib/model/migrations/legacy/20191231-02-add-schema-storage.js diff --git a/lib/model/migrations/20200110-01-add-drafts.js b/lib/model/migrations/legacy/20200110-01-add-drafts.js similarity index 100% rename from lib/model/migrations/20200110-01-add-drafts.js rename to lib/model/migrations/legacy/20200110-01-add-drafts.js diff --git a/lib/model/migrations/20200112-01-check-field-collisions.js b/lib/model/migrations/legacy/20200112-01-check-field-collisions.js similarity index 100% rename from lib/model/migrations/20200112-01-check-field-collisions.js rename to lib/model/migrations/legacy/20200112-01-check-field-collisions.js diff --git a/lib/model/migrations/20200114-01-remove-formid-sha256-constraint.js b/lib/model/migrations/legacy/20200114-01-remove-formid-sha256-constraint.js similarity index 100% rename from lib/model/migrations/20200114-01-remove-formid-sha256-constraint.js rename to lib/model/migrations/legacy/20200114-01-remove-formid-sha256-constraint.js diff --git a/lib/model/migrations/20200117-01-draft-test-submissions.js b/lib/model/migrations/legacy/20200117-01-draft-test-submissions.js similarity index 100% rename from lib/model/migrations/20200117-01-draft-test-submissions.js rename to lib/model/migrations/legacy/20200117-01-draft-test-submissions.js diff --git a/lib/model/migrations/20200121-01-add-draft-keys.js b/lib/model/migrations/legacy/20200121-01-add-draft-keys.js similarity index 100% rename from lib/model/migrations/20200121-01-add-draft-keys.js rename to lib/model/migrations/legacy/20200121-01-add-draft-keys.js diff --git a/lib/model/migrations/20200122-01-remove-draft-form-state.js b/lib/model/migrations/legacy/20200122-01-remove-draft-form-state.js similarity index 100% rename from lib/model/migrations/20200122-01-remove-draft-form-state.js rename to lib/model/migrations/legacy/20200122-01-remove-draft-form-state.js diff --git a/lib/model/migrations/20200129-01-cascade-submission-deletes.js b/lib/model/migrations/legacy/20200129-01-cascade-submission-deletes.js similarity index 100% rename from lib/model/migrations/20200129-01-cascade-submission-deletes.js rename to lib/model/migrations/legacy/20200129-01-cascade-submission-deletes.js diff --git a/lib/model/migrations/20200220-01-repair-submission-parsing.js b/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js similarity index 100% rename from lib/model/migrations/20200220-01-repair-submission-parsing.js rename to lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js diff --git a/lib/model/migrations/20200403-01-add-performance-indices.js b/lib/model/migrations/legacy/20200403-01-add-performance-indices.js similarity index 100% rename from lib/model/migrations/20200403-01-add-performance-indices.js rename to lib/model/migrations/legacy/20200403-01-add-performance-indices.js diff --git a/lib/model/migrations/20200407-01-allow-actorless-submission-defs.js b/lib/model/migrations/legacy/20200407-01-allow-actorless-submission-defs.js similarity index 100% rename from lib/model/migrations/20200407-01-allow-actorless-submission-defs.js rename to lib/model/migrations/legacy/20200407-01-allow-actorless-submission-defs.js diff --git a/lib/model/migrations/20200423-01-fix-field-insert-performance.js b/lib/model/migrations/legacy/20200423-01-fix-field-insert-performance.js similarity index 100% rename from lib/model/migrations/20200423-01-fix-field-insert-performance.js rename to lib/model/migrations/legacy/20200423-01-fix-field-insert-performance.js diff --git a/lib/model/migrations/20200428-01-allow-string-downcast.js b/lib/model/migrations/legacy/20200428-01-allow-string-downcast.js similarity index 100% rename from lib/model/migrations/20200428-01-allow-string-downcast.js rename to lib/model/migrations/legacy/20200428-01-allow-string-downcast.js diff --git a/lib/model/migrations/20200519-01-add-enketo-id.js b/lib/model/migrations/legacy/20200519-01-add-enketo-id.js similarity index 100% rename from lib/model/migrations/20200519-01-add-enketo-id.js rename to lib/model/migrations/legacy/20200519-01-add-enketo-id.js diff --git a/lib/model/migrations/20200519-02-add-form-viewer-role.js b/lib/model/migrations/legacy/20200519-02-add-form-viewer-role.js similarity index 100% rename from lib/model/migrations/20200519-02-add-form-viewer-role.js rename to lib/model/migrations/legacy/20200519-02-add-form-viewer-role.js diff --git a/lib/model/migrations/20200520-01-backfill-enketo.js b/lib/model/migrations/legacy/20200520-01-backfill-enketo.js similarity index 100% rename from lib/model/migrations/20200520-01-backfill-enketo.js rename to lib/model/migrations/legacy/20200520-01-backfill-enketo.js diff --git a/lib/model/migrations/20200715-01-add-data-collector-role.js b/lib/model/migrations/legacy/20200715-01-add-data-collector-role.js similarity index 100% rename from lib/model/migrations/20200715-01-add-data-collector-role.js rename to lib/model/migrations/legacy/20200715-01-add-data-collector-role.js diff --git a/lib/model/migrations/20200721-01-add-public-links.js b/lib/model/migrations/legacy/20200721-01-add-public-links.js similarity index 100% rename from lib/model/migrations/20200721-01-add-public-links.js rename to lib/model/migrations/legacy/20200721-01-add-public-links.js diff --git a/lib/model/migrations/20200728-01-add-enketo-single-token-to-forms.js b/lib/model/migrations/legacy/20200728-01-add-enketo-single-token-to-forms.js similarity index 100% rename from lib/model/migrations/20200728-01-add-enketo-single-token-to-forms.js rename to lib/model/migrations/legacy/20200728-01-add-enketo-single-token-to-forms.js diff --git a/lib/model/migrations/20200731-01-allow-project-managers-to-end-sessions.js b/lib/model/migrations/legacy/20200731-01-allow-project-managers-to-end-sessions.js similarity index 100% rename from lib/model/migrations/20200731-01-allow-project-managers-to-end-sessions.js rename to lib/model/migrations/legacy/20200731-01-allow-project-managers-to-end-sessions.js diff --git a/lib/model/migrations/20200810-01-reschedule-enketo-processing.js b/lib/model/migrations/legacy/20200810-01-reschedule-enketo-processing.js similarity index 100% rename from lib/model/migrations/20200810-01-reschedule-enketo-processing.js rename to lib/model/migrations/legacy/20200810-01-reschedule-enketo-processing.js diff --git a/lib/model/migrations/20200918-01-repair-publishedat-dates.js b/lib/model/migrations/legacy/20200918-01-repair-publishedat-dates.js similarity index 100% rename from lib/model/migrations/20200918-01-repair-publishedat-dates.js rename to lib/model/migrations/legacy/20200918-01-repair-publishedat-dates.js diff --git a/lib/model/migrations/20200930-01-add-backup-run-verb.js b/lib/model/migrations/legacy/20200930-01-add-backup-run-verb.js similarity index 100% rename from lib/model/migrations/20200930-01-add-backup-run-verb.js rename to lib/model/migrations/legacy/20200930-01-add-backup-run-verb.js diff --git a/lib/model/migrations/20201117-01-remove-deleted-actor-assignments-again.js b/lib/model/migrations/legacy/20201117-01-remove-deleted-actor-assignments-again.js similarity index 100% rename from lib/model/migrations/20201117-01-remove-deleted-actor-assignments-again.js rename to lib/model/migrations/legacy/20201117-01-remove-deleted-actor-assignments-again.js diff --git a/lib/model/migrations/20201207-01-harmonize-submitter-id-columns.js b/lib/model/migrations/legacy/20201207-01-harmonize-submitter-id-columns.js similarity index 100% rename from lib/model/migrations/20201207-01-harmonize-submitter-id-columns.js rename to lib/model/migrations/legacy/20201207-01-harmonize-submitter-id-columns.js diff --git a/lib/model/migrations/20210118-01-add-current-flag-to-submission-defs.js b/lib/model/migrations/legacy/20210118-01-add-current-flag-to-submission-defs.js similarity index 100% rename from lib/model/migrations/20210118-01-add-current-flag-to-submission-defs.js rename to lib/model/migrations/legacy/20210118-01-add-current-flag-to-submission-defs.js diff --git a/lib/model/migrations/20210120-01-instance-names.js b/lib/model/migrations/legacy/20210120-01-instance-names.js similarity index 100% rename from lib/model/migrations/20210120-01-instance-names.js rename to lib/model/migrations/legacy/20210120-01-instance-names.js diff --git a/lib/model/migrations/20210203-01-add-hierarchy-to-actees.js b/lib/model/migrations/legacy/20210203-01-add-hierarchy-to-actees.js similarity index 100% rename from lib/model/migrations/20210203-01-add-hierarchy-to-actees.js rename to lib/model/migrations/legacy/20210203-01-add-hierarchy-to-actees.js diff --git a/lib/model/migrations/20210210-01-add-instanceid-to-submission-defs.js b/lib/model/migrations/legacy/20210210-01-add-instanceid-to-submission-defs.js similarity index 100% rename from lib/model/migrations/20210210-01-add-instanceid-to-submission-defs.js rename to lib/model/migrations/legacy/20210210-01-add-instanceid-to-submission-defs.js diff --git a/lib/model/migrations/20210218-01-add-submission-edit-verbs.js b/lib/model/migrations/legacy/20210218-01-add-submission-edit-verbs.js similarity index 100% rename from lib/model/migrations/20210218-01-add-submission-edit-verbs.js rename to lib/model/migrations/legacy/20210218-01-add-submission-edit-verbs.js diff --git a/lib/model/migrations/20210218-02-add-draft-to-submissions-unique.js b/lib/model/migrations/legacy/20210218-02-add-draft-to-submissions-unique.js similarity index 100% rename from lib/model/migrations/20210218-02-add-draft-to-submissions-unique.js rename to lib/model/migrations/legacy/20210218-02-add-draft-to-submissions-unique.js diff --git a/lib/model/migrations/20210219-01-add-review-state.js b/lib/model/migrations/legacy/20210219-01-add-review-state.js similarity index 100% rename from lib/model/migrations/20210219-01-add-review-state.js rename to lib/model/migrations/legacy/20210219-01-add-review-state.js diff --git a/lib/model/migrations/20210219-02-add-notes-and-index-to-audits.js b/lib/model/migrations/legacy/20210219-02-add-notes-and-index-to-audits.js similarity index 100% rename from lib/model/migrations/20210219-02-add-notes-and-index-to-audits.js rename to lib/model/migrations/legacy/20210219-02-add-notes-and-index-to-audits.js diff --git a/lib/model/migrations/20210324-01-add-submission-edit-verbs-to-managers.js b/lib/model/migrations/legacy/20210324-01-add-submission-edit-verbs-to-managers.js similarity index 100% rename from lib/model/migrations/20210324-01-add-submission-edit-verbs-to-managers.js rename to lib/model/migrations/legacy/20210324-01-add-submission-edit-verbs-to-managers.js diff --git a/lib/model/migrations/20210325-01-remove-project.list-verb.js b/lib/model/migrations/legacy/20210325-01-remove-project.list-verb.js similarity index 100% rename from lib/model/migrations/20210325-01-remove-project.list-verb.js rename to lib/model/migrations/legacy/20210325-01-remove-project.list-verb.js diff --git a/lib/model/migrations/20210408-01-drop-public-link-createdat.js b/lib/model/migrations/legacy/20210408-01-drop-public-link-createdat.js similarity index 100% rename from lib/model/migrations/20210408-01-drop-public-link-createdat.js rename to lib/model/migrations/legacy/20210408-01-drop-public-link-createdat.js diff --git a/lib/model/migrations/20210408-02-backfill-specialized-actor-audits.js b/lib/model/migrations/legacy/20210408-02-backfill-specialized-actor-audits.js similarity index 100% rename from lib/model/migrations/20210408-02-backfill-specialized-actor-audits.js rename to lib/model/migrations/legacy/20210408-02-backfill-specialized-actor-audits.js diff --git a/lib/model/migrations/20210409-01-add-comments.js b/lib/model/migrations/legacy/20210409-01-add-comments.js similarity index 100% rename from lib/model/migrations/20210409-01-add-comments.js rename to lib/model/migrations/legacy/20210409-01-add-comments.js diff --git a/lib/model/migrations/20210409-02-update-review-states.js b/lib/model/migrations/legacy/20210409-02-update-review-states.js similarity index 100% rename from lib/model/migrations/20210409-02-update-review-states.js rename to lib/model/migrations/legacy/20210409-02-update-review-states.js diff --git a/lib/model/migrations/20210423-01-add-name-to-form-def.js b/lib/model/migrations/legacy/20210423-01-add-name-to-form-def.js similarity index 100% rename from lib/model/migrations/20210423-01-add-name-to-form-def.js rename to lib/model/migrations/legacy/20210423-01-add-name-to-form-def.js diff --git a/lib/model/migrations/20210423-02-drop-form-name.js b/lib/model/migrations/legacy/20210423-02-drop-form-name.js similarity index 100% rename from lib/model/migrations/20210423-02-drop-form-name.js rename to lib/model/migrations/legacy/20210423-02-drop-form-name.js diff --git a/lib/model/migrations/20210716-01-config-value-jsonb.js b/lib/model/migrations/legacy/20210716-01-config-value-jsonb.js similarity index 100% rename from lib/model/migrations/20210716-01-config-value-jsonb.js rename to lib/model/migrations/legacy/20210716-01-config-value-jsonb.js diff --git a/lib/model/migrations/20210721-01-add-config-set-verb.js b/lib/model/migrations/legacy/20210721-01-add-config-set-verb.js similarity index 100% rename from lib/model/migrations/20210721-01-add-config-set-verb.js rename to lib/model/migrations/legacy/20210721-01-add-config-set-verb.js diff --git a/lib/model/migrations/20210817-01-disallow-structure-downcast-to-string.js b/lib/model/migrations/legacy/20210817-01-disallow-structure-downcast-to-string.js similarity index 100% rename from lib/model/migrations/20210817-01-disallow-structure-downcast-to-string.js rename to lib/model/migrations/legacy/20210817-01-disallow-structure-downcast-to-string.js diff --git a/lib/model/migrations/20210825-01-add-analytics-read-verb.js b/lib/model/migrations/legacy/20210825-01-add-analytics-read-verb.js similarity index 100% rename from lib/model/migrations/20210825-01-add-analytics-read-verb.js rename to lib/model/migrations/legacy/20210825-01-add-analytics-read-verb.js diff --git a/lib/model/migrations/20210903-01-backfill-encrypted-client-audits.js b/lib/model/migrations/legacy/20210903-01-backfill-encrypted-client-audits.js similarity index 100% rename from lib/model/migrations/20210903-01-backfill-encrypted-client-audits.js rename to lib/model/migrations/legacy/20210903-01-backfill-encrypted-client-audits.js diff --git a/lib/model/migrations/20210927-01-revert-disallow-structure-downcast.js b/lib/model/migrations/legacy/20210927-01-revert-disallow-structure-downcast.js similarity index 100% rename from lib/model/migrations/20210927-01-revert-disallow-structure-downcast.js rename to lib/model/migrations/legacy/20210927-01-revert-disallow-structure-downcast.js diff --git a/lib/model/migrations/20211008-01-track-select-many-options.js b/lib/model/migrations/legacy/20211008-01-track-select-many-options.js similarity index 100% rename from lib/model/migrations/20211008-01-track-select-many-options.js rename to lib/model/migrations/legacy/20211008-01-track-select-many-options.js diff --git a/lib/model/migrations/20211021-remove-hashes-from-audits.js b/lib/model/migrations/legacy/20211021-remove-hashes-from-audits.js similarity index 100% rename from lib/model/migrations/20211021-remove-hashes-from-audits.js rename to lib/model/migrations/legacy/20211021-remove-hashes-from-audits.js diff --git a/lib/model/migrations/20211109-01-add-user-agent-to-submissions.js b/lib/model/migrations/legacy/20211109-01-add-user-agent-to-submissions.js similarity index 100% rename from lib/model/migrations/20211109-01-add-user-agent-to-submissions.js rename to lib/model/migrations/legacy/20211109-01-add-user-agent-to-submissions.js diff --git a/lib/model/migrations/20211114-01-flag-initial-submission-def.js b/lib/model/migrations/legacy/20211114-01-flag-initial-submission-def.js similarity index 100% rename from lib/model/migrations/20211114-01-flag-initial-submission-def.js rename to lib/model/migrations/legacy/20211114-01-flag-initial-submission-def.js diff --git a/lib/model/migrations/20211117-01-add-form-restore-verb.js b/lib/model/migrations/legacy/20211117-01-add-form-restore-verb.js similarity index 100% rename from lib/model/migrations/20211117-01-add-form-restore-verb.js rename to lib/model/migrations/legacy/20211117-01-add-form-restore-verb.js diff --git a/lib/model/migrations/20211129-01-add-purged-details-to-actees.js b/lib/model/migrations/legacy/20211129-01-add-purged-details-to-actees.js similarity index 100% rename from lib/model/migrations/20211129-01-add-purged-details-to-actees.js rename to lib/model/migrations/legacy/20211129-01-add-purged-details-to-actees.js diff --git a/lib/model/migrations/20220121-01-form-cascade-delete.js b/lib/model/migrations/legacy/20220121-01-form-cascade-delete.js similarity index 100% rename from lib/model/migrations/20220121-01-form-cascade-delete.js rename to lib/model/migrations/legacy/20220121-01-form-cascade-delete.js diff --git a/lib/model/migrations/20220121-02-purge-deleted-forms.js b/lib/model/migrations/legacy/20220121-02-purge-deleted-forms.js similarity index 100% rename from lib/model/migrations/20220121-02-purge-deleted-forms.js rename to lib/model/migrations/legacy/20220121-02-purge-deleted-forms.js diff --git a/lib/model/migrations/20220209-01-purge-unneeded-drafts.js b/lib/model/migrations/legacy/20220209-01-purge-unneeded-drafts.js similarity index 100% rename from lib/model/migrations/20220209-01-purge-unneeded-drafts.js rename to lib/model/migrations/legacy/20220209-01-purge-unneeded-drafts.js diff --git a/lib/model/migrations/20220309-01-add-project-description.js b/lib/model/migrations/legacy/20220309-01-add-project-description.js similarity index 100% rename from lib/model/migrations/20220309-01-add-project-description.js rename to lib/model/migrations/legacy/20220309-01-add-project-description.js diff --git a/lib/model/migrations/20220803-01-create-entities-schema.js b/lib/model/migrations/legacy/20220803-01-create-entities-schema.js similarity index 100% rename from lib/model/migrations/20220803-01-create-entities-schema.js rename to lib/model/migrations/legacy/20220803-01-create-entities-schema.js diff --git a/lib/model/migrations/20221003-01-add-dataset-verbs.js b/lib/model/migrations/legacy/20221003-01-add-dataset-verbs.js similarity index 100% rename from lib/model/migrations/20221003-01-add-dataset-verbs.js rename to lib/model/migrations/legacy/20221003-01-add-dataset-verbs.js diff --git a/lib/model/migrations/20221114-01-explict-dataset-publish.js b/lib/model/migrations/legacy/20221114-01-explict-dataset-publish.js similarity index 100% rename from lib/model/migrations/20221114-01-explict-dataset-publish.js rename to lib/model/migrations/legacy/20221114-01-explict-dataset-publish.js diff --git a/lib/model/migrations/20221117-01-check-datasetId-is-null-for-non-file-type.js b/lib/model/migrations/legacy/20221117-01-check-datasetId-is-null-for-non-file-type.js similarity index 100% rename from lib/model/migrations/20221117-01-check-datasetId-is-null-for-non-file-type.js rename to lib/model/migrations/legacy/20221117-01-check-datasetId-is-null-for-non-file-type.js diff --git a/lib/model/migrations/20221118-01-make-entities-columns-not-null.js b/lib/model/migrations/legacy/20221118-01-make-entities-columns-not-null.js similarity index 100% rename from lib/model/migrations/20221118-01-make-entities-columns-not-null.js rename to lib/model/migrations/legacy/20221118-01-make-entities-columns-not-null.js diff --git a/lib/model/migrations/20221208-01-reduce-tz-precision.js b/lib/model/migrations/legacy/20221208-01-reduce-tz-precision.js similarity index 100% rename from lib/model/migrations/20221208-01-reduce-tz-precision.js rename to lib/model/migrations/legacy/20221208-01-reduce-tz-precision.js diff --git a/lib/model/migrations/20230106-01-remove-revision-number.js b/lib/model/migrations/legacy/20230106-01-remove-revision-number.js similarity index 100% rename from lib/model/migrations/20230106-01-remove-revision-number.js rename to lib/model/migrations/legacy/20230106-01-remove-revision-number.js diff --git a/lib/model/migrations/20230109-01-add-form-schema.js b/lib/model/migrations/legacy/20230109-01-add-form-schema.js similarity index 100% rename from lib/model/migrations/20230109-01-add-form-schema.js rename to lib/model/migrations/legacy/20230109-01-add-form-schema.js diff --git a/lib/model/migrations/20230123-01-remove-google-backups.js b/lib/model/migrations/legacy/20230123-01-remove-google-backups.js similarity index 100% rename from lib/model/migrations/20230123-01-remove-google-backups.js rename to lib/model/migrations/legacy/20230123-01-remove-google-backups.js diff --git a/lib/model/migrations/20230126-01-add-entity-indices.js b/lib/model/migrations/legacy/20230126-01-add-entity-indices.js similarity index 100% rename from lib/model/migrations/20230126-01-add-entity-indices.js rename to lib/model/migrations/legacy/20230126-01-add-entity-indices.js diff --git a/lib/model/migrations/20230127-01-rename-entity-created-by.js b/lib/model/migrations/legacy/20230127-01-rename-entity-created-by.js similarity index 100% rename from lib/model/migrations/20230127-01-rename-entity-created-by.js rename to lib/model/migrations/legacy/20230127-01-rename-entity-created-by.js diff --git a/lib/model/migrations/20230324-01-edit-dataset-verbs.js b/lib/model/migrations/legacy/20230324-01-edit-dataset-verbs.js similarity index 100% rename from lib/model/migrations/20230324-01-edit-dataset-verbs.js rename to lib/model/migrations/legacy/20230324-01-edit-dataset-verbs.js diff --git a/lib/model/migrations/20230406-01-add-entity-def-fields.js b/lib/model/migrations/legacy/20230406-01-add-entity-def-fields.js similarity index 100% rename from lib/model/migrations/20230406-01-add-entity-def-fields.js rename to lib/model/migrations/legacy/20230406-01-add-entity-def-fields.js diff --git a/lib/model/migrations/20230406-02-move-entity-label-add-deletedAt.js b/lib/model/migrations/legacy/20230406-02-move-entity-label-add-deletedAt.js similarity index 100% rename from lib/model/migrations/20230406-02-move-entity-label-add-deletedAt.js rename to lib/model/migrations/legacy/20230406-02-move-entity-label-add-deletedAt.js diff --git a/lib/model/migrations/20230414-01-remove-user-mfa-secret.js b/lib/model/migrations/legacy/20230414-01-remove-user-mfa-secret.js similarity index 100% rename from lib/model/migrations/20230414-01-remove-user-mfa-secret.js rename to lib/model/migrations/legacy/20230414-01-remove-user-mfa-secret.js diff --git a/lib/model/migrations/20230419-01-optimize-indices-sub-defs.js b/lib/model/migrations/legacy/20230419-01-optimize-indices-sub-defs.js similarity index 100% rename from lib/model/migrations/20230419-01-optimize-indices-sub-defs.js rename to lib/model/migrations/legacy/20230419-01-optimize-indices-sub-defs.js diff --git a/lib/model/migrations/20230509-01-dataset-approval-flag.js b/lib/model/migrations/legacy/20230509-01-dataset-approval-flag.js similarity index 100% rename from lib/model/migrations/20230509-01-dataset-approval-flag.js rename to lib/model/migrations/legacy/20230509-01-dataset-approval-flag.js diff --git a/lib/model/migrations/20230512-01-add-entity-root.js b/lib/model/migrations/legacy/20230512-01-add-entity-root.js similarity index 100% rename from lib/model/migrations/20230512-01-add-entity-root.js rename to lib/model/migrations/legacy/20230512-01-add-entity-root.js diff --git a/lib/model/migrations/20230512-02-backfill-entity-id.js b/lib/model/migrations/legacy/20230512-02-backfill-entity-id.js similarity index 100% rename from lib/model/migrations/20230512-02-backfill-entity-id.js rename to lib/model/migrations/legacy/20230512-02-backfill-entity-id.js diff --git a/lib/model/migrations/20230512-03-add-entity-source.js b/lib/model/migrations/legacy/20230512-03-add-entity-source.js similarity index 100% rename from lib/model/migrations/20230512-03-add-entity-source.js rename to lib/model/migrations/legacy/20230512-03-add-entity-source.js diff --git a/lib/model/migrations/20230518-01-add-entity-index-to-audits.js b/lib/model/migrations/legacy/20230518-01-add-entity-index-to-audits.js similarity index 100% rename from lib/model/migrations/20230518-01-add-entity-index-to-audits.js rename to lib/model/migrations/legacy/20230518-01-add-entity-index-to-audits.js diff --git a/lib/model/migrations/20230802-01-delete-orphan-submissions.js b/lib/model/migrations/legacy/20230802-01-delete-orphan-submissions.js similarity index 100% rename from lib/model/migrations/20230802-01-delete-orphan-submissions.js rename to lib/model/migrations/legacy/20230802-01-delete-orphan-submissions.js diff --git a/lib/model/migrations/20230818-01-remove-schemaId-from-dsPropertyFields.js b/lib/model/migrations/legacy/20230818-01-remove-schemaId-from-dsPropertyFields.js similarity index 100% rename from lib/model/migrations/20230818-01-remove-schemaId-from-dsPropertyFields.js rename to lib/model/migrations/legacy/20230818-01-remove-schemaId-from-dsPropertyFields.js diff --git a/lib/model/migrations/20230824-01-add-entity-version.js b/lib/model/migrations/legacy/20230824-01-add-entity-version.js similarity index 100% rename from lib/model/migrations/20230824-01-add-entity-version.js rename to lib/model/migrations/legacy/20230824-01-add-entity-version.js diff --git a/lib/model/migrations/20230830-01-remove-entity-label-from-audits.js b/lib/model/migrations/legacy/20230830-01-remove-entity-label-from-audits.js similarity index 100% rename from lib/model/migrations/20230830-01-remove-entity-label-from-audits.js rename to lib/model/migrations/legacy/20230830-01-remove-entity-label-from-audits.js diff --git a/lib/model/migrations/20230907-01-opened-form-verb.js b/lib/model/migrations/legacy/20230907-01-opened-form-verb.js similarity index 100% rename from lib/model/migrations/20230907-01-opened-form-verb.js rename to lib/model/migrations/legacy/20230907-01-opened-form-verb.js diff --git a/lib/model/migrations/20231002-01-add-conflict-details.js b/lib/model/migrations/legacy/20231002-01-add-conflict-details.js similarity index 100% rename from lib/model/migrations/20231002-01-add-conflict-details.js rename to lib/model/migrations/legacy/20231002-01-add-conflict-details.js diff --git a/lib/model/migrations/20231013-01-change-entity-error-action.js b/lib/model/migrations/legacy/20231013-01-change-entity-error-action.js similarity index 100% rename from lib/model/migrations/20231013-01-change-entity-error-action.js rename to lib/model/migrations/legacy/20231013-01-change-entity-error-action.js diff --git a/lib/model/migrations/20231208-01-dataset-form-def-actions.js b/lib/model/migrations/legacy/20231208-01-dataset-form-def-actions.js similarity index 100% rename from lib/model/migrations/20231208-01-dataset-form-def-actions.js rename to lib/model/migrations/legacy/20231208-01-dataset-form-def-actions.js diff --git a/lib/model/migrations/20240215-01-entity-delete-verb.js b/lib/model/migrations/legacy/20240215-01-entity-delete-verb.js similarity index 100% rename from lib/model/migrations/20240215-01-entity-delete-verb.js rename to lib/model/migrations/legacy/20240215-01-entity-delete-verb.js diff --git a/lib/model/migrations/20240215-02-dedupe-verbs.js b/lib/model/migrations/legacy/20240215-02-dedupe-verbs.js similarity index 100% rename from lib/model/migrations/20240215-02-dedupe-verbs.js rename to lib/model/migrations/legacy/20240215-02-dedupe-verbs.js diff --git a/lib/model/migrations/20240312-01-add-dataset-create-verb.js b/lib/model/migrations/legacy/20240312-01-add-dataset-create-verb.js similarity index 100% rename from lib/model/migrations/20240312-01-add-dataset-create-verb.js rename to lib/model/migrations/legacy/20240312-01-add-dataset-create-verb.js diff --git a/lib/model/migrations/20240322-01-add-entity-source-index-to-audits.js b/lib/model/migrations/legacy/20240322-01-add-entity-source-index-to-audits.js similarity index 100% rename from lib/model/migrations/20240322-01-add-entity-source-index-to-audits.js rename to lib/model/migrations/legacy/20240322-01-add-entity-source-index-to-audits.js diff --git a/lib/model/migrations/20240515-01-entity-tz-precision.js b/lib/model/migrations/legacy/20240515-01-entity-tz-precision.js similarity index 100% rename from lib/model/migrations/20240515-01-entity-tz-precision.js rename to lib/model/migrations/legacy/20240515-01-entity-tz-precision.js diff --git a/lib/model/migrations/20240607-01-add-offline-entity-branch-trunk-info.js b/lib/model/migrations/legacy/20240607-01-add-offline-entity-branch-trunk-info.js similarity index 100% rename from lib/model/migrations/20240607-01-add-offline-entity-branch-trunk-info.js rename to lib/model/migrations/legacy/20240607-01-add-offline-entity-branch-trunk-info.js diff --git a/lib/model/migrations/20240607-02-add-submission-backlog.js b/lib/model/migrations/legacy/20240607-02-add-submission-backlog.js similarity index 100% rename from lib/model/migrations/20240607-02-add-submission-backlog.js rename to lib/model/migrations/legacy/20240607-02-add-submission-backlog.js diff --git a/lib/model/migrations/20240715-01-backlog-add-event-entityuuid.js b/lib/model/migrations/legacy/20240715-01-backlog-add-event-entityuuid.js similarity index 100% rename from lib/model/migrations/20240715-01-backlog-add-event-entityuuid.js rename to lib/model/migrations/legacy/20240715-01-backlog-add-event-entityuuid.js diff --git a/lib/model/migrations/20240913-01-add-blob-s3.js b/lib/model/migrations/legacy/20240913-01-add-blob-s3.js similarity index 100% rename from lib/model/migrations/20240913-01-add-blob-s3.js rename to lib/model/migrations/legacy/20240913-01-add-blob-s3.js diff --git a/lib/model/migrations/20240914-01-add-submission-delete-verb.js b/lib/model/migrations/legacy/20240914-01-add-submission-delete-verb.js similarity index 100% rename from lib/model/migrations/20240914-01-add-submission-delete-verb.js rename to lib/model/migrations/legacy/20240914-01-add-submission-delete-verb.js diff --git a/lib/model/migrations/20240914-02-remove-orphaned-client-audits.js b/lib/model/migrations/legacy/20240914-02-remove-orphaned-client-audits.js similarity index 100% rename from lib/model/migrations/20240914-02-remove-orphaned-client-audits.js rename to lib/model/migrations/legacy/20240914-02-remove-orphaned-client-audits.js diff --git a/lib/model/migrations/20241001-01-index-on-session-table.js b/lib/model/migrations/legacy/20241001-01-index-on-session-table.js similarity index 100% rename from lib/model/migrations/20241001-01-index-on-session-table.js rename to lib/model/migrations/legacy/20241001-01-index-on-session-table.js diff --git a/lib/model/migrations/20241008-01-add-user_preferences.js b/lib/model/migrations/legacy/20241008-01-add-user_preferences.js similarity index 100% rename from lib/model/migrations/20241008-01-add-user_preferences.js rename to lib/model/migrations/legacy/20241008-01-add-user_preferences.js diff --git a/lib/model/migrations/20241010-01-schedule-entity-form-upgrade.js b/lib/model/migrations/legacy/20241010-01-schedule-entity-form-upgrade.js similarity index 100% rename from lib/model/migrations/20241010-01-schedule-entity-form-upgrade.js rename to lib/model/migrations/legacy/20241010-01-schedule-entity-form-upgrade.js diff --git a/lib/model/migrations/20241029-01-schedule-entity-form-upgrade-create-forms.js b/lib/model/migrations/legacy/20241029-01-schedule-entity-form-upgrade-create-forms.js similarity index 100% rename from lib/model/migrations/20241029-01-schedule-entity-form-upgrade-create-forms.js rename to lib/model/migrations/legacy/20241029-01-schedule-entity-form-upgrade-create-forms.js diff --git a/lib/model/migrations/20241030-01-add-force-entity-def-source.js b/lib/model/migrations/legacy/20241030-01-add-force-entity-def-source.js similarity index 100% rename from lib/model/migrations/20241030-01-add-force-entity-def-source.js rename to lib/model/migrations/legacy/20241030-01-add-force-entity-def-source.js diff --git a/lib/util/db.js b/lib/util/db.js index 918fb3797..91f7ef85b 100644 --- a/lib/util/db.js +++ b/lib/util/db.js @@ -48,7 +48,7 @@ const connectionString = (config) => { }; // Returns an object that Knex will use to connect to the database. -const connectionObject = (config) => { +const knexConfig = (config) => { const problem = validateConfig(config); if (problem != null) throw problem; // We ignore maximumPoolSize when using Knex. @@ -588,7 +588,7 @@ const postgresErrorToProblem = (x) => { }; module.exports = { - connectionString, connectionObject, + connectionString, knexConfig, unjoiner, extender, sqlEquals, page, queryFuncs, insert, insertMany, updater, markDeleted, markUndeleted, QueryOptions, diff --git a/test/unit/util/db.js b/test/unit/util/db.js index 6a804d812..2bed332fe 100644 --- a/test/unit/util/db.js +++ b/test/unit/util/db.js @@ -97,11 +97,11 @@ describe('util/db', () => { }); }); - describe('connectionObject', () => { - const { connectionObject } = util; + describe('knexConfig', () => { + const { knexConfig } = util; it('should return an object with the required options', () => { - const result = connectionObject({ + const result = knexConfig({ host: 'localhost', database: 'foo', user: 'bar', @@ -116,7 +116,7 @@ describe('util/db', () => { }); it('should include the port if one is specified', () => { - const result = connectionObject({ + const result = knexConfig({ host: 'localhost', database: 'foo', user: 'bar', @@ -133,7 +133,7 @@ describe('util/db', () => { }); it('should return the correct object if ssl is true', () => { - const result = connectionObject({ + const result = knexConfig({ host: 'localhost', database: 'foo', user: 'bar', @@ -150,7 +150,7 @@ describe('util/db', () => { }); it('should throw if ssl is false', () => { - const result = () => connectionObject({ + const result = () => knexConfig({ host: 'localhost', database: 'foo', user: 'bar', @@ -161,7 +161,7 @@ describe('util/db', () => { }); it('should throw if ssl is an object', () => { - const result = () => connectionObject({ + const result = () => knexConfig({ host: 'localhost', database: 'foo', user: 'bar', @@ -172,7 +172,7 @@ describe('util/db', () => { }); it('should allow (but ignore) maximumPoolSize', () => { - const result = connectionObject({ + const result = knexConfig({ host: 'localhost', database: 'foo', user: 'bar', @@ -188,7 +188,7 @@ describe('util/db', () => { }); it('should throw for an unsupported option', () => { - const result = () => connectionObject({ + const result = () => knexConfig({ host: 'localhost', database: 'foo', user: 'bar', From 2d9ff155f8bb0b22990f2489ca55affd183916c2 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 09:41:00 +0000 Subject: [PATCH 03/44] Add db migration test from other PR --- ...isable-nullable-blob-content-types.spec.js | 58 +++++++++++++++++ test/db-migrations/utils.js | 63 +++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js diff --git a/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js b/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js new file mode 100644 index 000000000..e3fb41c03 --- /dev/null +++ b/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js @@ -0,0 +1,58 @@ +const assert = require('node:assert/strict'); +const { hash, randomBytes } = require('node:crypto'); + +const { // eslint-disable-line object-curly-newline + assertTableContents, + describeMigration, + rowsExistFor, +} = require('./utils'); // eslint-disable-line object-curly-newline + +describeMigration('20250113-01-disable-nullable-blob-content-types', ({ runMigrationBeingTested }) => { + const aBlobWith = props => { + const randomContent = randomBytes(100); + const md5 = hash('md5', randomContent); // eslint-disable-line no-multi-spaces + const sha = hash('sha1', randomContent); + return { md5, sha, ...props }; + }; + const aBlob = () => aBlobWith({}); + + const blob1 = aBlobWith({ contentType: null }); + const blob2 = aBlobWith({ contentType: 'text/plain' }); + + before(async () => { + await rowsExistFor('blobs', blob1, blob2); + + await runMigrationBeingTested(); + }); + + it('should change existing NULL contentType values to application/octet-stream, and preserve non-NULL values', async () => { + await assertTableContents('blobs', + { ...blob1, contentType: 'application/octet-stream' }, + { ...blob2, contentType: 'text/plain' }, + ); + }); + + it(`should create new blobs with contentType 'application/octet-stream' (contentType not supplied)`, async () => { + const { md5, sha } = aBlob(); + + const created = await db.oneFirst(sql` + INSERT INTO blobs (md5, sha, "contentType") + VALUES(${md5}, ${sha}, DEFAULT) + RETURNING "contentType" + `); + + assert.equal(created, 'application/octet-stream'); + }); + + it(`should create new blobs with contentType 'application/octet-stream' (supplied DEFAULT contentType)`, async () => { + const { md5, sha } = aBlob(); + + const created = await db.oneFirst(sql` + INSERT INTO blobs (md5, sha, "contentType") + VALUES(${md5}, ${sha}, DEFAULT) + RETURNING "contentType" + `); + + assert.equal(created, 'application/octet-stream'); + }); +}); diff --git a/test/db-migrations/utils.js b/test/db-migrations/utils.js index 85788ea01..43e811d2d 100644 --- a/test/db-migrations/utils.js +++ b/test/db-migrations/utils.js @@ -115,9 +115,72 @@ function assertIncludes(actual, expected) { } } +async function rowsExistFor(tableName, ...rows) { + if (!rows.length) throw new Error(`Attempted to insert 0 rows into table ${tableName}`); + + assertAllHaveSameProps(rows); // eslint-disable-line no-use-before-define + const colNames = Object.keys(rows[0]); + if (!colNames.length) throw new Error(`Attempted to insert data with 0 defined columns`); + + const table = sql.identifier([tableName]); + const cols = sql.join(colNames.map(k => sql.identifier([k])), sql`,`); + + return db.query( + sql` + INSERT INTO ${table} (${cols}) + SELECT ${cols} + FROM JSON_POPULATE_RECORDSET(NULL::${table}, ${JSON.stringify(rows)}) + `, + ); +} + +async function assertTableContents(tableName, ...expected) { + const { rows: actual } = await db.query(sql`SELECT * FROM ${sql.identifier([tableName])}`); + + assert.equal( + actual.length, + expected.length, + `Unexpected number of rows in table '${tableName}'. ` + + `Expected ${expected.length} but got ${actual.length}. ` + + `DB returned: ${JSON.stringify(actual, null, 2)}`, + ); + + const remainingRows = [ ...actual ]; + for (let i=0; i _.pick(r, Object.keys(x))); + assert.fail(`Expected row ${i} not found in table '${tableName}':\n json=${JSON.stringify({ remainingRows, filteredRemainingRows, expectedRow: x })}`); + } + } +} + +function assertAllHaveSameProps(list) { + if (list.length < 2) return; + const [ first, ...rest ] = list.map(Object.keys); + + rest.forEach((v, i) => { + assert.deepEqual(v, first, `Row #${i+1} has different props to row #0. All supplied rows must have the same props.`); + }); +} + module.exports = { assertIndexExists, + assertTableContents, assertTableDoesNotExist, assertTableSchema, + describeMigration, + + rowsExistFor, }; From 42023e07153c7752118b8a0c91a2204ef7885159 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 09:49:49 +0000 Subject: [PATCH 04/44] fix imports; lints --- lib/bin/run-migrations.js | 4 +-- lib/model/migrate.js | 35 +++++++++---------- .../legacy/20180727-02-add-md5-to-blobs.js | 2 +- .../20180727-03-add-form-attachments-table.js | 2 +- .../legacy/20190520-01-add-form-versioning.js | 2 +- .../20191007-01-backfill-client-audits.js | 6 ++-- .../legacy/20191231-02-add-schema-storage.js | 4 +-- .../20200220-01-repair-submission-parsing.js | 2 +- .../legacy/20210120-01-instance-names.js | 2 +- .../20211008-01-track-select-many-options.js | 8 ++--- .../legacy/20230109-01-add-form-schema.js | 2 +- lib/task/task.js | 4 +-- lib/util/db.js | 8 ++--- 13 files changed, 40 insertions(+), 41 deletions(-) diff --git a/lib/bin/run-migrations.js b/lib/bin/run-migrations.js index 93324f8c6..10e9d3edd 100644 --- a/lib/bin/run-migrations.js +++ b/lib/bin/run-migrations.js @@ -7,12 +7,12 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { +const { // eslint-disable-line object-curly-newline withKnex, knexMigrations, postKnexMigrations, -} = require('../model/migrate'); +} = require('../model/migrate'); // eslint-disable-line object-curly-newline (async () => { try { diff --git a/lib/model/migrate.js b/lib/model/migrate.js index 53c3b9ec7..982ea1bc6 100644 --- a/lib/model/migrate.js +++ b/lib/model/migrate.js @@ -12,10 +12,9 @@ const { lstatSync, readdirSync } = require('node:fs'); -const _ = require('lodash'); +const _ = require('lodash'); // eslint-disable-line import/no-extraneous-dependencies const knex = require('knex'); const pg = require('pg'); -const { Migrator } = require('knex/lib/migrate/Migrator'); const { knexConfig } = require('../util/db'); // Connects to the postgres database specified in configuration and returns it. @@ -31,7 +30,7 @@ const withKnex = (config) => (mutator) => { const knexMigrations = (db) => db.migrate.latest({ directory: `${__dirname}/migrations/legacy` }); const postKnexMigrations = async (config) => { - const log = (...args) => console.log('[postKnexMigrations]', ...args); + const log = (...args) => console.log('[postKnexMigrations]', ...args); // eslint-disable-line no-console log('ENTRY'); const { Client } = pg; @@ -74,10 +73,11 @@ const postKnexMigrations = async (config) => { // N.B. this table is created to be similar to the legacy knex-created table. // The key difference is that name, batch and migration_time columns are // not nullable. + const maxLen = 255; await client.query(` CREATE TABLE IF NOT EXISTS post_knex_migrations ( id SERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, + name VARCHAR(${maxLen}) NOT NULL, batch INTEGER NOT NULL, migration_time TIMESTAMP(3) WITH TIME ZONE NOT NULL )`); @@ -89,8 +89,8 @@ const postKnexMigrations = async (config) => { const migrationsDir = `${__dirname}/migrations`; const allMigrations = readdirSync(migrationsDir) - .filter(f => f.endsWith('.js') && lstatSync(`${migrationsDir}/${f}`).isFile()) - .sort(); + .filter(f => f.endsWith('.js') && lstatSync(`${migrationsDir}/${f}`).isFile()) + .sort(); log('allMigrations:', allMigrations); const alreadyRun = (await client.query('SELECT name FROM post_knex_migrations')).rows.map(r => r.name); @@ -101,35 +101,35 @@ const postKnexMigrations = async (config) => { const toRun = toRunNames.map(name => { const path = `${migrationsDir}/${name}`; - const migration = require(path); + const migration = require(path); // eslint-disable-line import/no-dynamic-require return { name, path, migration }; }); log('toRun:', toRun); - if(!toRun.length) { + if (!toRun.length) { log('No migrations to run - exiting.'); await client.query('ROLLBACK'); return; } log('Validating', toRun.length, 'migrations...'); - for(const { migration, name, path } of toRun) { + for (const { migration, name } of toRun) { log('Validing migration:', name, '...'); - if(name.length > 255) throw new Error(`Migration name '${name}' is too long - max length is ${maxLen}, but got ${name.length}`); + if (name.length > maxLen) throw new Error(`Migration name '${name}' is too long - max length is ${maxLen}, but got ${name.length}`); // TODO check for illegal chars in name? const keys = Object.keys(migration); const unexpectedKeys = _.omit(keys, 'up', 'down'); - if(unexpectedKeys.length) throw new Error(`Unexpected key(s) found in migration ${name}: ${unexpectedKeys}`); + if (unexpectedKeys.length) throw new Error(`Unexpected key(s) found in migration ${name}: ${unexpectedKeys}`); - if(!migration.up) throw new Error(`Required prop .up not found in migration ${name}`); - if(typeof migration.up !== 'function') { + if (!migration.up) throw new Error(`Required prop .up not found in migration ${name}`); + if (typeof migration.up !== 'function') { throw new Error(`Required prop .up of migration ${name} has incorrect type - expected 'function', but got '${typeof migration.up}'`); } - if(migration.down && typeof migration.down !== 'function') { + if (migration.down && typeof migration.down !== 'function') { throw new Error(`Optional prop .down of migration ${name} has incorrect type - expected 'function' but got '${typeof migration.down}'`); } @@ -138,9 +138,9 @@ const postKnexMigrations = async (config) => { log(toRun.length, 'migrations look valid.'); log('Running', toRun.length, 'migrations...'); - for (const { migration, name, path } of toRun) { + for (const { migration, name } of toRun) { log('Running migration:', name); - await migration.up(client); + await migration.up(client); // eslint-disable-line no-await-in-loop log('Migration complete:', name); } log(toRun.length, 'migrations ran OK.'); @@ -150,7 +150,7 @@ const postKnexMigrations = async (config) => { // Note that migration_time is CLOCK_TIMESTAMP() to match knex implementation. // TODO confirm in relevant version of knex source code that this is actually the case, and link here. - const namesJson = JSON.stringify(toRun.map(m => m.name)); + const namesJson = JSON.stringify(toRun.map(m => m.name)); // See: https://www.postgresql.org/docs/current/functions-json.html await client.query(` INSERT INTO post_knex_migrations(name, batch, migration_time) @@ -183,4 +183,3 @@ const checkMigrations = (db) => db.migrate.list({ directory: `${__dirname}/migra }); module.exports = { checkMigrations, initKnex, withKnex, knexMigrations, postKnexMigrations }; - diff --git a/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js b/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js index de05f99a4..8c487f7ae 100644 --- a/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js +++ b/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js @@ -8,7 +8,7 @@ // except according to the terms contained in the LICENSE file. // -const { md5sum } = require('../../util/crypto'); +const { md5sum } = require('../../../util/crypto'); const up = (knex) => knex.schema.table('blobs', (blobs) => { blobs.string('md5', 32); }) diff --git a/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js b/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js index 80aea61ab..1633829ae 100644 --- a/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js +++ b/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js @@ -23,7 +23,7 @@ const up = (knex) => fa.index([ 'formId' ]); }).then(() => { - const { expectedFormAttachments } = require('../../data/schema'); + const { expectedFormAttachments } = require('../../../data/schema'); const { uniq, pluck } = require('ramda'); // now add all expected attachments on extant forms. diff --git a/lib/model/migrations/legacy/20190520-01-add-form-versioning.js b/lib/model/migrations/legacy/20190520-01-add-form-versioning.js index 2d11c8ce3..8b8e9c490 100644 --- a/lib/model/migrations/legacy/20190520-01-add-form-versioning.js +++ b/lib/model/migrations/legacy/20190520-01-add-form-versioning.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { shasum, sha256sum } = require('../../util/crypto'); +const { shasum, sha256sum } = require('../../../util/crypto'); const assert = require('assert').strict; const check = (message, query) => diff --git a/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js b/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js index c6551c255..9067ccc06 100644 --- a/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js +++ b/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js @@ -7,9 +7,9 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { parseClientAudits } = require('../../data/client-audits'); -const { getFormFields } = require('../../data/schema'); -const { traverseXml, findOne, root, node, text } = require('../../util/xml'); +const { parseClientAudits } = require('../../../data/client-audits'); +const { getFormFields } = require('../../../data/schema'); +const { traverseXml, findOne, root, node, text } = require('../../../util/xml'); const up = (db) => new Promise((resolve, reject) => { const work = []; diff --git a/lib/model/migrations/legacy/20191231-02-add-schema-storage.js b/lib/model/migrations/legacy/20191231-02-add-schema-storage.js index ec317539e..f331a1eaa 100644 --- a/lib/model/migrations/legacy/20191231-02-add-schema-storage.js +++ b/lib/model/migrations/legacy/20191231-02-add-schema-storage.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { getFormFields } = require('../../data/schema'); +const { getFormFields } = require('../../../data/schema'); const up = async (db) => { await db.schema.createTable('form_fields', (fields) => { @@ -51,7 +51,7 @@ const up = async (db) => { // this config hardcoding would be dangerous with tests except that // tests will never invoke this path. const config = require('config').get('default.database'); - const db2 = require('../migrate').connect(config); + const db2 = require('../../migrate').connect(config); return db2.select('projectId', 'xmlFormId').from('forms').where({ currentDefId: formDefId }) .then(([{ projectId, xmlFormId }]) => { process.stderr.write(`\n!!!!\nThe database upgrade to v0.8 has failed because the Form '${xmlFormId}' in Project ${projectId} has an invalid schema. It tries to bind multiple instance nodes at the path ${path}.\n!!!!\n\n`); diff --git a/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js b/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js index d1bd9e0ee..f6f671ef3 100644 --- a/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js +++ b/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { Submission } = require('../frames'); +const { Submission } = require('../../frames'); const up = async (db) => { const work = []; diff --git a/lib/model/migrations/legacy/20210120-01-instance-names.js b/lib/model/migrations/legacy/20210120-01-instance-names.js index 832407dfc..36c625343 100644 --- a/lib/model/migrations/legacy/20210120-01-instance-names.js +++ b/lib/model/migrations/legacy/20210120-01-instance-names.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { Submission } = require('../frames'); +const { Submission } = require('../../frames'); const up = async (db) => { await db.schema.table('submission_defs', (sds) => { diff --git a/lib/model/migrations/legacy/20211008-01-track-select-many-options.js b/lib/model/migrations/legacy/20211008-01-track-select-many-options.js index 413e0f1aa..caa466186 100644 --- a/lib/model/migrations/legacy/20211008-01-track-select-many-options.js +++ b/lib/model/migrations/legacy/20211008-01-track-select-many-options.js @@ -8,10 +8,10 @@ // except according to the terms contained in the LICENSE file. const { map } = require('ramda'); -const { getFormFields } = require('../../data/schema'); -const { getSelectMultipleResponses } = require('../../data/submission'); -const { Form } = require('../frames'); -const { construct } = require('../../util/util'); +const { getFormFields } = require('../../../data/schema'); +const { getSelectMultipleResponses } = require('../../../data/submission'); +const { Form } = require('../../frames'); +const { construct } = require('../../../util/util'); const up = async (db) => { // add select many flag, options field to fields diff --git a/lib/model/migrations/legacy/20230109-01-add-form-schema.js b/lib/model/migrations/legacy/20230109-01-add-form-schema.js index 3f591473e..40013f840 100644 --- a/lib/model/migrations/legacy/20230109-01-add-form-schema.js +++ b/lib/model/migrations/legacy/20230109-01-add-form-schema.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { getFormFields, compare } = require('../../data/schema'); +const { getFormFields, compare } = require('../../../data/schema'); /* Steps of this migration 1. remove check field collision trigger diff --git a/lib/task/task.js b/lib/task/task.js index f72fa23e2..6f9a1eb13 100644 --- a/lib/task/task.js +++ b/lib/task/task.js @@ -75,7 +75,7 @@ const writeTo = (output) => (x) => output.write(`${x}\n`); const writeToStderr = writeTo(process.stderr); /* istanbul ignore next */ const fault = (error) => { - console.log('fault()', error); + console.log('fault()', error); // eslint-disable-line no-console // first print our error. if ((error != null) && (error.isProblem === true) && (error.httpCode < 500)) { writeToStderr(error.message); @@ -106,7 +106,7 @@ const auditing = (action, t) => ((typeof t === 'function') return Promise.resolve(result); }) )), - ((error) => console.log('auditing() error', error) || auditLog(action, false, Option.of(error).map(Problem.serializable).orNull()).then( + ((error) => console.log('auditing() error', error) || auditLog(action, false, Option.of(error).map(Problem.serializable).orNull()).then( // eslint-disable-line no-console (() => Promise.reject(error)), ((auditError) => { writeToStderr('Failed to audit-log task failure message!'); diff --git a/lib/util/db.js b/lib/util/db.js index 91f7ef85b..b85e6b9e3 100644 --- a/lib/util/db.js +++ b/lib/util/db.js @@ -52,16 +52,16 @@ const knexConfig = (config) => { const problem = validateConfig(config); if (problem != null) throw problem; // We ignore maximumPoolSize when using Knex. - const { maximumPoolSize, ...knexConfig } = config; - if (knexConfig.ssl === true) { + const { maximumPoolSize, ...rest } = config; + if (rest.ssl === true) { // Slonik seems to specify `false` for `rejectUnauthorized` whenever SSL is // specified: // https://github.com/gajus/slonik/issues/159#issuecomment-891089466. We do // the same here so that Knex will connect to the database in the same way // as Slonik. - knexConfig.ssl = { rejectUnauthorized: false }; + rest.ssl = { rejectUnauthorized: false }; } - return knexConfig; + return rest; }; From c838cf6c4a72e69c51e989924cbfe140b43ebd2a Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 10:04:41 +0000 Subject: [PATCH 05/44] Add eslint rules --- lib/model/migrations/.eslintrc.json | 9 +++++++++ lib/model/migrations/legacy/.eslintrc.json | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 lib/model/migrations/.eslintrc.json create mode 100644 lib/model/migrations/legacy/.eslintrc.json diff --git a/lib/model/migrations/.eslintrc.json b/lib/model/migrations/.eslintrc.json new file mode 100644 index 000000000..ca7bff3bc --- /dev/null +++ b/lib/model/migrations/.eslintrc.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../.eslintrc.json", + "rules": { + "no-restricted-modules": [ + "error", + { "patterns": [ "../*" ] } + ] + } +} diff --git a/lib/model/migrations/legacy/.eslintrc.json b/lib/model/migrations/legacy/.eslintrc.json new file mode 100644 index 000000000..27d39fc27 --- /dev/null +++ b/lib/model/migrations/legacy/.eslintrc.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../../.eslintrc.json", + "rules": { + "no-restricted-modules": [ + "warn", + { "patterns": [ "../*" ] } + ] + } +} From a9aea26a8f4c0aaf86c5a2377401e59dbc674686 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 10:05:00 +0000 Subject: [PATCH 06/44] whitespace --- lib/model/migrations/.eslintrc.json | 12 ++++++------ lib/model/migrations/legacy/.eslintrc.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/model/migrations/.eslintrc.json b/lib/model/migrations/.eslintrc.json index ca7bff3bc..f34c5819a 100644 --- a/lib/model/migrations/.eslintrc.json +++ b/lib/model/migrations/.eslintrc.json @@ -1,9 +1,9 @@ { "extends": "../../../.eslintrc.json", - "rules": { - "no-restricted-modules": [ - "error", - { "patterns": [ "../*" ] } - ] - } + "rules": { + "no-restricted-modules": [ + "error", + { "patterns": [ "../*" ] } + ] + } } diff --git a/lib/model/migrations/legacy/.eslintrc.json b/lib/model/migrations/legacy/.eslintrc.json index 27d39fc27..4fe3ba4a9 100644 --- a/lib/model/migrations/legacy/.eslintrc.json +++ b/lib/model/migrations/legacy/.eslintrc.json @@ -1,9 +1,9 @@ { "extends": "../../../../.eslintrc.json", - "rules": { - "no-restricted-modules": [ - "warn", - { "patterns": [ "../*" ] } - ] - } + "rules": { + "no-restricted-modules": [ + "warn", + { "patterns": [ "../*" ] } + ] + } } From 609c34c7da0fcbdc2800dbd006c67e5a607ca8a8 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 10:06:56 +0000 Subject: [PATCH 07/44] rename withDatabase() --- lib/bin/check-open-db-queries.js | 4 ++-- .../other/{migrations.js => knex-migrations.js} | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) rename test/integration/other/{migrations.js => knex-migrations.js} (99%) diff --git a/lib/bin/check-open-db-queries.js b/lib/bin/check-open-db-queries.js index 603776d09..3299f839a 100644 --- a/lib/bin/check-open-db-queries.js +++ b/lib/bin/check-open-db-queries.js @@ -7,11 +7,11 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { withDatabase } = require('../model/migrate'); +const { withKnex } = require('../model/migrate'); (async () => { try { - const { rows } = await withDatabase(require('config').get('default.database'))((db) => + const { rows } = await withKnex(require('config').get('default.database'))((db) => db.raw('SELECT COUNT(*) FROM pg_stat_activity WHERE usename=CURRENT_USER')); const queryCount = rows[0].count - 1; // the count query will appear as one of the open queries diff --git a/test/integration/other/migrations.js b/test/integration/other/knex-migrations.js similarity index 99% rename from test/integration/other/migrations.js rename to test/integration/other/knex-migrations.js index b582c83da..9b51e3aac 100644 --- a/test/integration/other/migrations.js +++ b/test/integration/other/knex-migrations.js @@ -6,7 +6,7 @@ const { testContainerFullTrx, testServiceFullTrx } = require('../setup'); const { sql } = require('slonik'); const { createReadStream } = require('fs'); const { Actor, Config } = require(appRoot + '/lib/model/frames'); -const { withDatabase } = require(appRoot + '/lib/model/migrate'); +const { withKnex } = require(appRoot + '/lib/model/migrate'); const { exhaust } = require(appRoot + '/lib/worker/worker'); const testData = require('../../data/xml'); @@ -14,8 +14,8 @@ const populateUsers = require('../fixtures/01-users'); const populateForms = require('../fixtures/02-forms'); const { getFormFields } = require('../../../lib/data/schema'); -const withTestDatabase = withDatabase(config.get('test.database')); -const migrationsDir = appRoot + '/lib/model/migrations'; +const withTestDatabase = withKnex(config.get('test.database')); +const migrationsDir = appRoot + '/lib/model/migrations/legacy'; const upToMigration = (toName, inclusive = true) => withTestDatabase(async (migrator) => { await migrator.raw('drop owned by current_user'); const migrations = await migrator.migrate.list({ directory: migrationsDir }); @@ -59,7 +59,7 @@ testMigration.skip = (filename, tests) => // column to projects and forms, it is not possible to migrate part way // (before the new column) and populate the data when frames expect the // new column to exist. -describe.skip('database migrations', function() { +describe.skip('legacy (knex) database migrations', function() { this.timeout(8000); it('should purge deleted forms via migration', testServiceFullTrx(async (service, container) => { From c2ce4baf713e6ca80327008014fd4913f363a1af Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 10:11:18 +0000 Subject: [PATCH 08/44] lint? --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8760c350e..22921d682 100644 --- a/Makefile +++ b/Makefile @@ -131,7 +131,8 @@ test-coverage: node_version .PHONY: lint lint: node_version - npx eslint --cache --max-warnings 0 . + # max-warnings set to take account of legacy database migrations + npx eslint --cache --max-warnings 15 . ################################################################################ From 714985f332392b14ee3dd53e507ab9c40def6be9 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 10:26:26 +0000 Subject: [PATCH 09/44] make sure client closed --- lib/bin/check-migrations.js | 6 ++- lib/model/migrate.js | 98 +++++++++++++++++++++++++------------ 2 files changed, 70 insertions(+), 34 deletions(-) diff --git a/lib/bin/check-migrations.js b/lib/bin/check-migrations.js index 20b9c5e82..65180cd11 100644 --- a/lib/bin/check-migrations.js +++ b/lib/bin/check-migrations.js @@ -7,11 +7,13 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { withKnex, checkMigrations } = require('../model/migrate'); +const { withKnex, checkKnexMigrations, checkPostKnexMigrations } = require('../model/migrate'); (async () => { try { - await withKnex(require('config').get('default.database'))(checkMigrations); + const config = require('config').get('default.database'); + await withKnex(config)(checkKnexMigrations); + await checkPostKnexMigrations(config); } catch (err) { console.error('Error:', err.message); process.exit(1); diff --git a/lib/model/migrate.js b/lib/model/migrate.js index 982ea1bc6..fa436665d 100644 --- a/lib/model/migrate.js +++ b/lib/model/migrate.js @@ -20,6 +20,9 @@ const { knexConfig } = require('../util/db'); // Connects to the postgres database specified in configuration and returns it. const initKnex = (config) => knex({ client: 'pg', connection: knexConfig(config) }); +const legacyPath = `${__dirname}/migrations/legacy`; +const postKnexPath = `${__dirname}/migrations`; + // Connects to a database, passes it to a function for operations, then ensures its closure. const withKnex = (config) => (mutator) => { const db = initKnex(config); @@ -27,10 +30,10 @@ const withKnex = (config) => (mutator) => { }; // Given a database, initiates migrations on it. -const knexMigrations = (db) => db.migrate.latest({ directory: `${__dirname}/migrations/legacy` }); +const knexMigrations = (db) => db.migrate.latest({ directory: legacyPath }); -const postKnexMigrations = async (config) => { - const log = (...args) => console.log('[postKnexMigrations]', ...args); // eslint-disable-line no-console +const getPostKnexClient = async config => { + const log = (...args) => console.log('[getPostKnexClient]', ...args); // eslint-disable-line no-console log('ENTRY'); const { Client } = pg; @@ -38,6 +41,45 @@ const postKnexMigrations = async (config) => { log('client created'); + log('Connecting to client...'); + await client.connect(); + log('Client connected OK.'); + + return client; +}; + +const getPostKnexMigrationsToRun = async client => { + const log = (...args) => console.log('[getPostKnexMigrationsToRun]', ...args); // eslint-disable-line no-console + log('ENTRY'); + + const migrationsDir = postKnexPath; + const allMigrations = readdirSync(migrationsDir) + .filter(f => f.endsWith('.js') && lstatSync(`${migrationsDir}/${f}`).isFile()) + .sort(); + log('allMigrations:', allMigrations); + + const alreadyRun = (await client.query('SELECT name FROM post_knex_migrations')).rows.map(r => r.name); + log('alreadyRun:', alreadyRun); + + const toRunNames = allMigrations.filter(m => !alreadyRun.includes(m)); + log('toRunNames:', toRunNames); + + const toRun = toRunNames.map(name => { + const path = `${migrationsDir}/${name}`; + const migration = require(path); // eslint-disable-line import/no-dynamic-require + return { name, path, migration }; + }); + log('toRun:', toRun); + + return toRun; +}; + +const postKnexMigrations = async (config) => { + const log = (...args) => console.log('[postKnexMigrations]', ...args); // eslint-disable-line no-console + log('ENTRY'); + + const client = await getPostKnexClient(config); + // In the main, this migrator is written to behave similarly to knex's: // // * expects transaction property async .up({ raw }) @@ -53,14 +95,6 @@ const postKnexMigrations = async (config) => { // * sets migration_time to be the start of the migration batch's transaction rather than some other intermediate time try { - log('Connecting to client...'); - await client.connect(); - log('Client connected OK.'); - - log('Testing client...'); - const res = await client.query('SELECT NOW()'); - log('Client returned:', res); - log('Starting transaction...'); await client.query('BEGIN'); // TODO do we need a specific transaction type? log('Transaction started.'); @@ -87,24 +121,7 @@ const postKnexMigrations = async (config) => { await client.query('LOCK TABLE post_knex_migrations IN EXCLUSIVE MODE NOWAIT'); log('Lock acquired.'); - const migrationsDir = `${__dirname}/migrations`; - const allMigrations = readdirSync(migrationsDir) - .filter(f => f.endsWith('.js') && lstatSync(`${migrationsDir}/${f}`).isFile()) - .sort(); - log('allMigrations:', allMigrations); - - const alreadyRun = (await client.query('SELECT name FROM post_knex_migrations')).rows.map(r => r.name); - log('alreadyRun:', alreadyRun); - - const toRunNames = allMigrations.filter(m => !alreadyRun.includes(m)); - log('toRunNames:', toRunNames); - - const toRun = toRunNames.map(name => { - const path = `${migrationsDir}/${name}`; - const migration = require(path); // eslint-disable-line import/no-dynamic-require - return { name, path, migration }; - }); - log('toRun:', toRun); + const toRun = getPostKnexMigrationsToRun(client); if (!toRun.length) { log('No migrations to run - exiting.'); @@ -169,17 +186,34 @@ const postKnexMigrations = async (config) => { throw err; } finally { log('Ending client...'); - await client.end(); // TODO needs await or callback? + await client.end(); log('Client ended.'); } }; // Checks for pending migrations and returns an exit code of 1 if any are // still pending/unapplied (e.g. automatically running migrations just failed). -const checkMigrations = (db) => db.migrate.list({ directory: `${__dirname}/migrations` }) +const checkKnexMigrations = (db) => db.migrate.list({ directory: legacyPath }) .then((res) => { if (res[1].length > 0) process.exitCode = 1; }); -module.exports = { checkMigrations, initKnex, withKnex, knexMigrations, postKnexMigrations }; +// Checks for pending migrations and returns an exit code of 1 if any are +// still pending/unapplied (e.g. automatically running migrations just failed). +const checkPostKnexMigrations = async config => { + const log = (...args) => console.log('[checkPostKnexMigrations]', ...args); // eslint-disable-line no-console + log('ENTRY'); + + const client = await getPostKnexClient(config); + try { + const toRun = await getPostKnexMigrationsToRun(client); + if (toRun.length) process.exitCode = 1; + } finally { + log('Ending client...'); + await client.end(); + log('Client ended.'); + } +}; + +module.exports = { checkKnexMigrations, checkPostKnexMigrations, initKnex, withKnex, knexMigrations, postKnexMigrations }; From 72bb6b0436e1e02c8177d0beb832b9068ae1f71c Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 10:28:17 +0000 Subject: [PATCH 10/44] add comment --- lib/bin/check-migrations.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/bin/check-migrations.js b/lib/bin/check-migrations.js index 65180cd11..2119c203d 100644 --- a/lib/bin/check-migrations.js +++ b/lib/bin/check-migrations.js @@ -9,6 +9,8 @@ const { withKnex, checkKnexMigrations, checkPostKnexMigrations } = require('../model/migrate'); +// REVIEW why is check-migrations required in the first place? + (async () => { try { const config = require('config').get('default.database'); From 65e7709e1b77e487c885e7f0feaa5c821fa1fa76 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 10:31:53 +0000 Subject: [PATCH 11/44] await --- lib/model/migrate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/model/migrate.js b/lib/model/migrate.js index fa436665d..dccdc5057 100644 --- a/lib/model/migrate.js +++ b/lib/model/migrate.js @@ -121,7 +121,7 @@ const postKnexMigrations = async (config) => { await client.query('LOCK TABLE post_knex_migrations IN EXCLUSIVE MODE NOWAIT'); log('Lock acquired.'); - const toRun = getPostKnexMigrationsToRun(client); + const toRun = await getPostKnexMigrationsToRun(client); if (!toRun.length) { log('No migrations to run - exiting.'); From 2fceecfd1d805ce5cf80751b65423a35f8a6f91f Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 10:35:53 +0000 Subject: [PATCH 12/44] handle connection closing --- lib/model/migrate.js | 189 +++++++++++++++++++++---------------------- 1 file changed, 93 insertions(+), 96 deletions(-) diff --git a/lib/model/migrate.js b/lib/model/migrate.js index dccdc5057..d940771f1 100644 --- a/lib/model/migrate.js +++ b/lib/model/migrate.js @@ -32,8 +32,8 @@ const withKnex = (config) => (mutator) => { // Given a database, initiates migrations on it. const knexMigrations = (db) => db.migrate.latest({ directory: legacyPath }); -const getPostKnexClient = async config => { - const log = (...args) => console.log('[getPostKnexClient]', ...args); // eslint-disable-line no-console +const withPg = async (config, fn) => { + const log = (...args) => console.log('[withPg]', ...args); // eslint-disable-line no-console log('ENTRY'); const { Client } = pg; @@ -45,7 +45,13 @@ const getPostKnexClient = async config => { await client.connect(); log('Client connected OK.'); - return client; + try { + await fn(client); + } finally { + log('Ending client...'); + await client.end(); + log('Client ended.'); + } }; const getPostKnexMigrationsToRun = async client => { @@ -78,8 +84,6 @@ const postKnexMigrations = async (config) => { const log = (...args) => console.log('[postKnexMigrations]', ...args); // eslint-disable-line no-console log('ENTRY'); - const client = await getPostKnexClient(config); - // In the main, this migrator is written to behave similarly to knex's: // // * expects transaction property async .up({ raw }) @@ -94,101 +98,99 @@ const postKnexMigrations = async (config) => { // * gets list of migrations to run _after_ acquiring db lock // * sets migration_time to be the start of the migration batch's transaction rather than some other intermediate time - try { - log('Starting transaction...'); - await client.query('BEGIN'); // TODO do we need a specific transaction type? - log('Transaction started.'); - - log('Acquiring knex lock...'); - // TODO do this... if it's useful. Need to think of _some_ way to prevent new migrations and old migrations running simultaneously. - log('Knex lock acquired'); - - log('Creating new table if not exists...'); - // N.B. this table is created to be similar to the legacy knex-created table. - // The key difference is that name, batch and migration_time columns are - // not nullable. - const maxLen = 255; - await client.query(` - CREATE TABLE IF NOT EXISTS post_knex_migrations ( - id SERIAL PRIMARY KEY, - name VARCHAR(${maxLen}) NOT NULL, - batch INTEGER NOT NULL, - migration_time TIMESTAMP(3) WITH TIME ZONE NOT NULL - )`); - log('Table now definitely exists.'); - - log('Acquiring lock on post_knex_migrations table...'); - await client.query('LOCK TABLE post_knex_migrations IN EXCLUSIVE MODE NOWAIT'); - log('Lock acquired.'); + await withPg(config, async client => { + try { + log('Starting transaction...'); + await client.query('BEGIN'); // TODO do we need a specific transaction type? + log('Transaction started.'); + + log('Acquiring knex lock...'); + // TODO do this... if it's useful. Need to think of _some_ way to prevent new migrations and old migrations running simultaneously. + log('Knex lock acquired'); + + log('Creating new table if not exists...'); + // N.B. this table is created to be similar to the legacy knex-created table. + // The key difference is that name, batch and migration_time columns are + // not nullable. + const maxLen = 255; + await client.query(` + CREATE TABLE IF NOT EXISTS post_knex_migrations ( + id SERIAL PRIMARY KEY, + name VARCHAR(${maxLen}) NOT NULL, + batch INTEGER NOT NULL, + migration_time TIMESTAMP(3) WITH TIME ZONE NOT NULL + )`); + log('Table now definitely exists.'); + + log('Acquiring lock on post_knex_migrations table...'); + await client.query('LOCK TABLE post_knex_migrations IN EXCLUSIVE MODE NOWAIT'); + log('Lock acquired.'); + + const toRun = await getPostKnexMigrationsToRun(client); + + if (!toRun.length) { + log('No migrations to run - exiting.'); + await client.query('ROLLBACK'); + return; + } - const toRun = await getPostKnexMigrationsToRun(client); + log('Validating', toRun.length, 'migrations...'); + for (const { migration, name } of toRun) { + log('Validing migration:', name, '...'); - if (!toRun.length) { - log('No migrations to run - exiting.'); - await client.query('ROLLBACK'); - return; - } + if (name.length > maxLen) throw new Error(`Migration name '${name}' is too long - max length is ${maxLen}, but got ${name.length}`); - log('Validating', toRun.length, 'migrations...'); - for (const { migration, name } of toRun) { - log('Validing migration:', name, '...'); + // TODO check for illegal chars in name? - if (name.length > maxLen) throw new Error(`Migration name '${name}' is too long - max length is ${maxLen}, but got ${name.length}`); + const keys = Object.keys(migration); + const unexpectedKeys = _.omit(keys, 'up', 'down'); + if (unexpectedKeys.length) throw new Error(`Unexpected key(s) found in migration ${name}: ${unexpectedKeys}`); - // TODO check for illegal chars in name? + if (!migration.up) throw new Error(`Required prop .up not found in migration ${name}`); + if (typeof migration.up !== 'function') { + throw new Error(`Required prop .up of migration ${name} has incorrect type - expected 'function', but got '${typeof migration.up}'`); + } - const keys = Object.keys(migration); - const unexpectedKeys = _.omit(keys, 'up', 'down'); - if (unexpectedKeys.length) throw new Error(`Unexpected key(s) found in migration ${name}: ${unexpectedKeys}`); + if (migration.down && typeof migration.down !== 'function') { + throw new Error(`Optional prop .down of migration ${name} has incorrect type - expected 'function' but got '${typeof migration.down}'`); + } - if (!migration.up) throw new Error(`Required prop .up not found in migration ${name}`); - if (typeof migration.up !== 'function') { - throw new Error(`Required prop .up of migration ${name} has incorrect type - expected 'function', but got '${typeof migration.up}'`); + log('Migration', name, 'looks valid.'); } + log(toRun.length, 'migrations look valid.'); - if (migration.down && typeof migration.down !== 'function') { - throw new Error(`Optional prop .down of migration ${name} has incorrect type - expected 'function' but got '${typeof migration.down}'`); + log('Running', toRun.length, 'migrations...'); + for (const { migration, name } of toRun) { + log('Running migration:', name); + await migration.up(client); // eslint-disable-line no-await-in-loop + log('Migration complete:', name); } - - log('Migration', name, 'looks valid.'); - } - log(toRun.length, 'migrations look valid.'); - - log('Running', toRun.length, 'migrations...'); - for (const { migration, name } of toRun) { - log('Running migration:', name); - await migration.up(client); // eslint-disable-line no-await-in-loop - log('Migration complete:', name); + log(toRun.length, 'migrations ran OK.'); + + const { lastBatch } = (await client.query(`SELECT COALESCE(MAX(batch), 0) AS "lastBatch" FROM post_knex_migrations`)).rows[0]; + log('lastBatch:', lastBatch); + + // Note that migration_time is CLOCK_TIMESTAMP() to match knex implementation. + // TODO confirm in relevant version of knex source code that this is actually the case, and link here. + const namesJson = JSON.stringify(toRun.map(m => m.name)); + // See: https://www.postgresql.org/docs/current/functions-json.html + await client.query(` + INSERT INTO post_knex_migrations(name, batch, migration_time) + SELECT value#>>'{}' AS name + , ${lastBatch + 1} AS batch + , CLOCK_TIMESTAMP() AS migration_time + FROM JSON_ARRAY_ELEMENTS($1) + `, [ namesJson ]); + + log('Committing migrations...'); + await client.query('COMMIT'); + log('Migrations committed.'); + } catch (err) { + log('Caught error; rolling back', err); + await client.query('ROLLBACK'); + throw err; } - log(toRun.length, 'migrations ran OK.'); - - const { lastBatch } = (await client.query(`SELECT COALESCE(MAX(batch), 0) AS "lastBatch" FROM post_knex_migrations`)).rows[0]; - log('lastBatch:', lastBatch); - - // Note that migration_time is CLOCK_TIMESTAMP() to match knex implementation. - // TODO confirm in relevant version of knex source code that this is actually the case, and link here. - const namesJson = JSON.stringify(toRun.map(m => m.name)); - // See: https://www.postgresql.org/docs/current/functions-json.html - await client.query(` - INSERT INTO post_knex_migrations(name, batch, migration_time) - SELECT value#>>'{}' AS name - , ${lastBatch + 1} AS batch - , CLOCK_TIMESTAMP() AS migration_time - FROM JSON_ARRAY_ELEMENTS($1) - `, [ namesJson ]); - - log('Committing migrations...'); - await client.query('COMMIT'); - log('Migrations committed.'); - } catch (err) { - log('Caught error; rolling back', err); - await client.query('ROLLBACK'); - throw err; - } finally { - log('Ending client...'); - await client.end(); - log('Client ended.'); - } + }); }; // Checks for pending migrations and returns an exit code of 1 if any are @@ -205,15 +207,10 @@ const checkPostKnexMigrations = async config => { const log = (...args) => console.log('[checkPostKnexMigrations]', ...args); // eslint-disable-line no-console log('ENTRY'); - const client = await getPostKnexClient(config); - try { + await withPg(config, async client => { const toRun = await getPostKnexMigrationsToRun(client); if (toRun.length) process.exitCode = 1; - } finally { - log('Ending client...'); - await client.end(); - log('Client ended.'); - } + }); }; module.exports = { checkKnexMigrations, checkPostKnexMigrations, initKnex, withKnex, knexMigrations, postKnexMigrations }; From 2e42824456ce02994a88c71afcc3d8e8cef94008 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 14 Jan 2025 10:50:45 +0000 Subject: [PATCH 13/44] run migrations in tests another way --- lib/model/migrate.js | 2 +- test/integration/setup.js | 21 ++++++--------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/lib/model/migrate.js b/lib/model/migrate.js index d940771f1..79444538b 100644 --- a/lib/model/migrate.js +++ b/lib/model/migrate.js @@ -213,4 +213,4 @@ const checkPostKnexMigrations = async config => { }); }; -module.exports = { checkKnexMigrations, checkPostKnexMigrations, initKnex, withKnex, knexMigrations, postKnexMigrations }; +module.exports = { checkKnexMigrations, checkPostKnexMigrations, initKnex, withKnex, withPg, knexMigrations, postKnexMigrations }; diff --git a/test/integration/setup.js b/test/integration/setup.js index 883ebeeeb..5db664093 100644 --- a/test/integration/setup.js +++ b/test/integration/setup.js @@ -1,3 +1,4 @@ +const { execSync } = require('node:child_process'); const { readFileSync } = require('fs'); const appRoot = require('app-root-path'); const { mergeRight } = require('ramda'); @@ -5,18 +6,18 @@ const { sql } = require('slonik'); const { readdirSync } = require('fs'); const { join } = require('path'); const request = require('supertest'); -const { noop } = require(appRoot + '/lib/util/util'); const { task } = require(appRoot + '/lib/task/task'); const authenticateUser = require('../util/authenticate-user'); const testData = require('../data/xml'); // knex things. const config = require('config'); -const { connect } = require(appRoot + '/lib/model/migrate'); +const { withPg } = require(appRoot + '/lib/model/migrate'); // slonik connection pool const { slonikPool } = require(appRoot + '/lib/external/slonik'); -const db = slonikPool(config.get('test.database')); +const dbConfig = config.get('test.database'); +const db = slonikPool(dbConfig); // set up our mailer. const env = config.get('default.env'); @@ -72,18 +73,8 @@ const populate = (container, [ head, ...tail ] = fixtures) => // this hook won't run if `test-unit` is called, as this directory is skipped // in that case. const initialize = async () => { - const migrator = connect(config.get('test.database')); - const { log } = console; - try { - await migrator.raw('drop owned by current_user'); - // Silence logging from migrations. - console.log = noop; // eslint-disable-line no-console - await migrator.migrate.latest({ directory: appRoot + '/lib/model/migrations' }); - } finally { - console.log = log; // eslint-disable-line no-console - await migrator.destroy(); - } - + await withPg(dbConfig, client => client.query('DROP OWNED BY CURRENT_USER')); + execSync('make migrations', { env: { ...process.env, NODE_CONFIG: JSON.stringify({ default: { database: dbConfig } }) } }); return withDefaults({ db, context, enketo, env, s3 }).transacting(populate); }; From 0821bdde1ba52d3c8b11b5ff5019229ecd001186 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Thu, 16 Jan 2025 05:53:23 +0000 Subject: [PATCH 14/44] rename knexConnection in line with master changes --- lib/model/knexfile.js | 4 ++-- lib/model/migrate.js | 4 ++-- lib/util/db.js | 4 ++-- test/unit/util/db.js | 18 +++++++++--------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/model/knexfile.js b/lib/model/knexfile.js index 511584825..9de8069fd 100644 --- a/lib/model/knexfile.js +++ b/lib/model/knexfile.js @@ -28,10 +28,10 @@ NODE_CONFIG_DIR=../../config DEBUG=knex:query,knex:bindings npx knex migrate:up */ const config = require('config'); -const { knexConfig } = require('../util/db'); +const { knexConnection } = require('../util/db'); module.exports = { client: 'pg', - connection: knexConfig(config.get('default.database')) + connection: knexConnection(config.get('default.database')) }; diff --git a/lib/model/migrate.js b/lib/model/migrate.js index 79444538b..63d48eb10 100644 --- a/lib/model/migrate.js +++ b/lib/model/migrate.js @@ -15,10 +15,10 @@ const { lstatSync, readdirSync } = require('node:fs'); const _ = require('lodash'); // eslint-disable-line import/no-extraneous-dependencies const knex = require('knex'); const pg = require('pg'); -const { knexConfig } = require('../util/db'); +const { knexConnection } = require('../util/db'); // Connects to the postgres database specified in configuration and returns it. -const initKnex = (config) => knex({ client: 'pg', connection: knexConfig(config) }); +const initKnex = (config) => knex({ client: 'pg', connection: knexConnection(config) }); const legacyPath = `${__dirname}/migrations/legacy`; const postKnexPath = `${__dirname}/migrations`; diff --git a/lib/util/db.js b/lib/util/db.js index b85e6b9e3..a095fa400 100644 --- a/lib/util/db.js +++ b/lib/util/db.js @@ -48,7 +48,7 @@ const connectionString = (config) => { }; // Returns an object that Knex will use to connect to the database. -const knexConfig = (config) => { +const knexConnection = (config) => { const problem = validateConfig(config); if (problem != null) throw problem; // We ignore maximumPoolSize when using Knex. @@ -588,7 +588,7 @@ const postgresErrorToProblem = (x) => { }; module.exports = { - connectionString, knexConfig, + connectionString, knexConnection, unjoiner, extender, sqlEquals, page, queryFuncs, insert, insertMany, updater, markDeleted, markUndeleted, QueryOptions, diff --git a/test/unit/util/db.js b/test/unit/util/db.js index 2bed332fe..21742afb6 100644 --- a/test/unit/util/db.js +++ b/test/unit/util/db.js @@ -97,11 +97,11 @@ describe('util/db', () => { }); }); - describe('knexConfig', () => { - const { knexConfig } = util; + describe('knexConnection', () => { + const { knexConnection } = util; it('should return an object with the required options', () => { - const result = knexConfig({ + const result = knexConnection({ host: 'localhost', database: 'foo', user: 'bar', @@ -116,7 +116,7 @@ describe('util/db', () => { }); it('should include the port if one is specified', () => { - const result = knexConfig({ + const result = knexConnection({ host: 'localhost', database: 'foo', user: 'bar', @@ -133,7 +133,7 @@ describe('util/db', () => { }); it('should return the correct object if ssl is true', () => { - const result = knexConfig({ + const result = knexConnection({ host: 'localhost', database: 'foo', user: 'bar', @@ -150,7 +150,7 @@ describe('util/db', () => { }); it('should throw if ssl is false', () => { - const result = () => knexConfig({ + const result = () => knexConnection({ host: 'localhost', database: 'foo', user: 'bar', @@ -161,7 +161,7 @@ describe('util/db', () => { }); it('should throw if ssl is an object', () => { - const result = () => knexConfig({ + const result = () => knexConnection({ host: 'localhost', database: 'foo', user: 'bar', @@ -172,7 +172,7 @@ describe('util/db', () => { }); it('should allow (but ignore) maximumPoolSize', () => { - const result = knexConfig({ + const result = knexConnection({ host: 'localhost', database: 'foo', user: 'bar', @@ -188,7 +188,7 @@ describe('util/db', () => { }); it('should throw for an unsupported option', () => { - const result = () => knexConfig({ + const result = () => knexConnection({ host: 'localhost', database: 'foo', user: 'bar', From 100c424af77c87f1ac19794f8eb9c48873ec5dd7 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Thu, 16 Jan 2025 06:19:20 +0000 Subject: [PATCH 15/44] Change in line with #1371 --- Makefile | 3 +-- lib/model/migrations/.eslintrc.js | 11 +++++++++++ lib/model/migrations/.eslintrc.json | 9 --------- lib/model/migrations/legacy/.eslintrc.json | 9 --------- .../migrations/legacy/20180727-02-add-md5-to-blobs.js | 2 +- .../legacy/20180727-03-add-form-attachments-table.js | 2 +- .../legacy/20190520-01-add-form-versioning.js | 2 +- .../legacy/20191007-01-backfill-client-audits.js | 6 +++--- .../legacy/20191231-02-add-schema-storage.js | 4 ++-- .../legacy/20200220-01-repair-submission-parsing.js | 2 +- .../migrations/legacy/20210120-01-instance-names.js | 2 +- .../legacy/20211008-01-track-select-many-options.js | 8 ++++---- .../migrations/legacy/20230109-01-add-form-schema.js | 2 +- 13 files changed, 27 insertions(+), 35 deletions(-) create mode 100644 lib/model/migrations/.eslintrc.js delete mode 100644 lib/model/migrations/.eslintrc.json delete mode 100644 lib/model/migrations/legacy/.eslintrc.json diff --git a/Makefile b/Makefile index 22921d682..8760c350e 100644 --- a/Makefile +++ b/Makefile @@ -131,8 +131,7 @@ test-coverage: node_version .PHONY: lint lint: node_version - # max-warnings set to take account of legacy database migrations - npx eslint --cache --max-warnings 15 . + npx eslint --cache --max-warnings 0 . ################################################################################ diff --git a/lib/model/migrations/.eslintrc.js b/lib/model/migrations/.eslintrc.js new file mode 100644 index 000000000..ed8ad55c1 --- /dev/null +++ b/lib/model/migrations/.eslintrc.js @@ -0,0 +1,11 @@ +module.exports = { + extends: '../../../.eslintrc.json', + rules: { + // Prevent migrations from including application code. + // + // Mixing application code into database migrations removes assurances that + // the same migration will mutate the same postgresql data identically when + // upgrading from/to different versions of the application. + 'no-restricted-modules': [ 'error', { patterns: [ '../*' ] } ], + }, +}; diff --git a/lib/model/migrations/.eslintrc.json b/lib/model/migrations/.eslintrc.json deleted file mode 100644 index f34c5819a..000000000 --- a/lib/model/migrations/.eslintrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../../.eslintrc.json", - "rules": { - "no-restricted-modules": [ - "error", - { "patterns": [ "../*" ] } - ] - } -} diff --git a/lib/model/migrations/legacy/.eslintrc.json b/lib/model/migrations/legacy/.eslintrc.json deleted file mode 100644 index 4fe3ba4a9..000000000 --- a/lib/model/migrations/legacy/.eslintrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../../../.eslintrc.json", - "rules": { - "no-restricted-modules": [ - "warn", - { "patterns": [ "../*" ] } - ] - } -} diff --git a/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js b/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js index 8c487f7ae..5fa121135 100644 --- a/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js +++ b/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js @@ -8,7 +8,7 @@ // except according to the terms contained in the LICENSE file. // -const { md5sum } = require('../../../util/crypto'); +const { md5sum } = require('../../../util/crypto'); // eslint-disable-line no-restricted-modules const up = (knex) => knex.schema.table('blobs', (blobs) => { blobs.string('md5', 32); }) diff --git a/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js b/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js index 1633829ae..c48ceb758 100644 --- a/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js +++ b/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js @@ -23,7 +23,7 @@ const up = (knex) => fa.index([ 'formId' ]); }).then(() => { - const { expectedFormAttachments } = require('../../../data/schema'); + const { expectedFormAttachments } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules const { uniq, pluck } = require('ramda'); // now add all expected attachments on extant forms. diff --git a/lib/model/migrations/legacy/20190520-01-add-form-versioning.js b/lib/model/migrations/legacy/20190520-01-add-form-versioning.js index 8b8e9c490..623e966c9 100644 --- a/lib/model/migrations/legacy/20190520-01-add-form-versioning.js +++ b/lib/model/migrations/legacy/20190520-01-add-form-versioning.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { shasum, sha256sum } = require('../../../util/crypto'); +const { shasum, sha256sum } = require('../../../util/crypto'); // eslint-disable-line no-restricted-modules const assert = require('assert').strict; const check = (message, query) => diff --git a/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js b/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js index 9067ccc06..5f79a3821 100644 --- a/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js +++ b/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js @@ -7,9 +7,9 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { parseClientAudits } = require('../../../data/client-audits'); -const { getFormFields } = require('../../../data/schema'); -const { traverseXml, findOne, root, node, text } = require('../../../util/xml'); +const { parseClientAudits } = require('../../../data/client-audits'); // eslint-disable-line no-restricted-modules +const { getFormFields } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules +const { traverseXml, findOne, root, node, text } = require('../../../util/xml'); // eslint-disable-line no-restricted-modules const up = (db) => new Promise((resolve, reject) => { const work = []; diff --git a/lib/model/migrations/legacy/20191231-02-add-schema-storage.js b/lib/model/migrations/legacy/20191231-02-add-schema-storage.js index f331a1eaa..da84931f6 100644 --- a/lib/model/migrations/legacy/20191231-02-add-schema-storage.js +++ b/lib/model/migrations/legacy/20191231-02-add-schema-storage.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { getFormFields } = require('../../../data/schema'); +const { getFormFields } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules const up = async (db) => { await db.schema.createTable('form_fields', (fields) => { @@ -51,7 +51,7 @@ const up = async (db) => { // this config hardcoding would be dangerous with tests except that // tests will never invoke this path. const config = require('config').get('default.database'); - const db2 = require('../../migrate').connect(config); + const db2 = require('../../migrate').connect(config); // eslint-disable-line no-restricted-modules return db2.select('projectId', 'xmlFormId').from('forms').where({ currentDefId: formDefId }) .then(([{ projectId, xmlFormId }]) => { process.stderr.write(`\n!!!!\nThe database upgrade to v0.8 has failed because the Form '${xmlFormId}' in Project ${projectId} has an invalid schema. It tries to bind multiple instance nodes at the path ${path}.\n!!!!\n\n`); diff --git a/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js b/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js index f6f671ef3..d4020bc25 100644 --- a/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js +++ b/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { Submission } = require('../../frames'); +const { Submission } = require('../../frames'); // eslint-disable-line no-restricted-modules const up = async (db) => { const work = []; diff --git a/lib/model/migrations/legacy/20210120-01-instance-names.js b/lib/model/migrations/legacy/20210120-01-instance-names.js index 36c625343..6c6cde9d9 100644 --- a/lib/model/migrations/legacy/20210120-01-instance-names.js +++ b/lib/model/migrations/legacy/20210120-01-instance-names.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { Submission } = require('../../frames'); +const { Submission } = require('../../frames'); // eslint-disable-line no-restricted-modules const up = async (db) => { await db.schema.table('submission_defs', (sds) => { diff --git a/lib/model/migrations/legacy/20211008-01-track-select-many-options.js b/lib/model/migrations/legacy/20211008-01-track-select-many-options.js index caa466186..09f1efa92 100644 --- a/lib/model/migrations/legacy/20211008-01-track-select-many-options.js +++ b/lib/model/migrations/legacy/20211008-01-track-select-many-options.js @@ -8,10 +8,10 @@ // except according to the terms contained in the LICENSE file. const { map } = require('ramda'); -const { getFormFields } = require('../../../data/schema'); -const { getSelectMultipleResponses } = require('../../../data/submission'); -const { Form } = require('../../frames'); -const { construct } = require('../../../util/util'); +const { getFormFields } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules +const { getSelectMultipleResponses } = require('../../../data/submission'); // eslint-disable-line no-restricted-modules +const { Form } = require('../../frames'); // eslint-disable-line no-restricted-modules +const { construct } = require('../../../util/util'); // eslint-disable-line no-restricted-modules const up = async (db) => { // add select many flag, options field to fields diff --git a/lib/model/migrations/legacy/20230109-01-add-form-schema.js b/lib/model/migrations/legacy/20230109-01-add-form-schema.js index 40013f840..63c2cd903 100644 --- a/lib/model/migrations/legacy/20230109-01-add-form-schema.js +++ b/lib/model/migrations/legacy/20230109-01-add-form-schema.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { getFormFields, compare } = require('../../../data/schema'); +const { getFormFields, compare } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules /* Steps of this migration 1. remove check field collision trigger From bb0d883af410f616ac8645286fe62019f4a896db Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Thu, 16 Jan 2025 06:24:11 +0000 Subject: [PATCH 16/44] js -> json --- lib/model/migrations/.eslintrc.js | 11 ----------- lib/model/migrations/.eslintrc.json | 6 ++++++ 2 files changed, 6 insertions(+), 11 deletions(-) delete mode 100644 lib/model/migrations/.eslintrc.js create mode 100644 lib/model/migrations/.eslintrc.json diff --git a/lib/model/migrations/.eslintrc.js b/lib/model/migrations/.eslintrc.js deleted file mode 100644 index ed8ad55c1..000000000 --- a/lib/model/migrations/.eslintrc.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - extends: '../../../.eslintrc.json', - rules: { - // Prevent migrations from including application code. - // - // Mixing application code into database migrations removes assurances that - // the same migration will mutate the same postgresql data identically when - // upgrading from/to different versions of the application. - 'no-restricted-modules': [ 'error', { patterns: [ '../*' ] } ], - }, -}; diff --git a/lib/model/migrations/.eslintrc.json b/lib/model/migrations/.eslintrc.json new file mode 100644 index 000000000..93a99d9dd --- /dev/null +++ b/lib/model/migrations/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "../../../.eslintrc.json", + "rules": { + "no-restricted-modules": [ "error", { "patterns": [ "../*" ] } ] + } +} From e2d64cc2352fe6e600d6c00bf8f9def507ef6e49 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Thu, 16 Jan 2025 06:26:54 +0000 Subject: [PATCH 17/44] reduce changes by renaming migration dirs --- lib/model/migrate.js | 4 ++-- lib/model/migrations-post-knex/.eslintrc.json | 6 ++++++ .../20250113-01-disable-nullable-blob-content-types.js | 0 lib/model/migrations/{legacy => }/20170920-01-initial.js | 0 lib/model/migrations/{legacy => }/20171010-01-auth.js | 0 .../migrations/{legacy => }/20171023-01-authz-forms.js | 0 .../{legacy => }/20171030-01-add-default-authz-records.js | 0 .../20171106-01-remove-user-update-timestamp.js | 0 .../{legacy => }/20171121-01-add-submissions-constraint.js | 0 .../migrations/{legacy => }/20171121-02-add-submitter.js | 0 .../{legacy => }/20171213-01-unrequire-display-name.js | 0 .../migrations/{legacy => }/20180108-01-expiring-actors.js | 0 .../migrations/{legacy => }/20180108-02-enum-to-varchar.js | 0 .../migrations/{legacy => }/20180112-01-audit-table.js | 0 .../migrations/{legacy => }/20180112-02-add-field-keys.js | 0 .../{legacy => }/20180118-01-rerequire-display-name.js | 0 .../{legacy => }/20180125-01-add-form-detail-fields.js | 0 .../{legacy => }/20180125-02-more-field-key-grants.js | 0 .../migrations/{legacy => }/20180125-03-add-blob-tables.js | 0 .../migrations/{legacy => }/20180301-01-configuration.js | 0 .../{legacy => }/20180322-01-additional-form-options.js | 0 .../{legacy => }/20180327-01-update-form-constraints.js | 0 .../{legacy => }/20180501-01-add-configs-timestamp.js | 0 .../migrations/{legacy => }/20180501-02-fix-date-columns.js | 0 .../20180515-01-enforce-nonnull-form-version.js | 0 .../{legacy => }/20180727-01-rename-attachments-table.js | 0 .../migrations/{legacy => }/20180727-02-add-md5-to-blobs.js | 0 .../{legacy => }/20180727-03-add-form-attachments-table.js | 0 .../{legacy => }/20181011-make-email-case-insensitive.js | 0 .../20181012-01-add-submissions-createdat-index.js | 0 .../migrations/{legacy => }/20181206-01-add-projects.js | 0 .../{legacy => }/20181207-01-grant-verbs-to-text.js | 0 .../{legacy => }/20181207-02-rename-grant-verbs.js | 0 .../{legacy => }/20181211-01-audit-verbs-to-text.js | 0 .../{legacy => }/20181211-02-rename-audit-actions.js | 0 .../migrations/{legacy => }/20181212-00-fix-user-type.js | 0 lib/model/migrations/{legacy => }/20181212-01-add-roles.js | 0 .../migrations/{legacy => }/20181212-02-remove-groups.js | 0 .../{legacy => }/20181212-03-add-single-use-roles.js | 0 .../{legacy => }/20181219-01-add-submission-update-verb.js | 0 .../{legacy => }/20181221-01-nullable-submission-blobs.js | 0 .../{legacy => }/20181230-01-add-device-id-to-submission.js | 0 .../{legacy => }/20190225-01-add-actor-trigram-indices.js | 0 .../migrations/{legacy => }/20190225-02-add-role-grants.js | 0 .../{legacy => }/20190226-01-convert-verbs-to-jsonb.js | 0 .../{legacy => }/20190226-02-add-role-actee-species.js | 0 .../{legacy => }/20190226-03-add-assignment-verbs.js | 0 .../20190226-04-add-assignment-actee-species.js | 0 .../{legacy => }/20190227-01-add-project-manager-role.js | 0 .../{legacy => }/20190405-01-add-project-archival-flag.js | 0 .../migrations/{legacy => }/20190416-01-email-uniqueness.js | 0 .../{legacy => }/20190416-02-add-user-delete-verb.js | 0 .../{legacy => }/20190520-01-add-form-versioning.js | 0 .../{legacy => }/20190523-01-add-form-state-constraint.js | 0 .../migrations/{legacy => }/20190605-01-reformat-audits.js | 0 .../20190607-01-convert-audit-details-to-jsonb.js | 0 .../20190607-02-standardize-attachment-actees.js | 0 .../20190607-03-rename-sub-attachment-audits.js | 0 .../migrations/{legacy => }/20190610-01-add-audits-verbs.js | 0 .../20190610-02-backfill-submission-audit-instanceids.js | 0 .../20190611-01-add-updatedat-to-form-attachments.js | 0 .../migrations/{legacy => }/20190618-01-add-csrf-token.js | 0 .../{legacy => }/20190618-01-add-encryption-tracking.js | 0 .../20190701-01-add-managed-encryption-key-check.js | 0 .../20190916-01-granularize-app-user-permissions.js | 0 .../{legacy => }/20190917-01-cleanup-app-user-role.js | 0 .../{legacy => }/20190923-01-add-project-viewer-role.js | 0 .../{legacy => }/20190925-01-add-client-audits.js | 0 .../{legacy => }/20191007-01-backfill-client-audits.js | 0 .../{legacy => }/20191010-01-add-excel-blob-reference.js | 0 .../20191023-01-add-worker-columns-to-audits.js | 0 .../migrations/{legacy => }/20191025-01-add-id-to-audits.js | 0 .../20191106-01-remove-deleted-actor-assignments.js | 0 .../{legacy => }/20191231-01-remove-transformations.js | 0 .../{legacy => }/20191231-02-add-schema-storage.js | 0 lib/model/migrations/{legacy => }/20200110-01-add-drafts.js | 0 .../{legacy => }/20200112-01-check-field-collisions.js | 0 .../20200114-01-remove-formid-sha256-constraint.js | 0 .../{legacy => }/20200117-01-draft-test-submissions.js | 0 .../migrations/{legacy => }/20200121-01-add-draft-keys.js | 0 .../{legacy => }/20200122-01-remove-draft-form-state.js | 0 .../{legacy => }/20200129-01-cascade-submission-deletes.js | 0 .../{legacy => }/20200220-01-repair-submission-parsing.js | 0 .../{legacy => }/20200403-01-add-performance-indices.js | 0 .../20200407-01-allow-actorless-submission-defs.js | 0 .../20200423-01-fix-field-insert-performance.js | 0 .../{legacy => }/20200428-01-allow-string-downcast.js | 0 .../migrations/{legacy => }/20200519-01-add-enketo-id.js | 0 .../{legacy => }/20200519-02-add-form-viewer-role.js | 0 .../migrations/{legacy => }/20200520-01-backfill-enketo.js | 0 .../{legacy => }/20200715-01-add-data-collector-role.js | 0 .../migrations/{legacy => }/20200721-01-add-public-links.js | 0 .../20200728-01-add-enketo-single-token-to-forms.js | 0 .../20200731-01-allow-project-managers-to-end-sessions.js | 0 .../20200810-01-reschedule-enketo-processing.js | 0 .../{legacy => }/20200918-01-repair-publishedat-dates.js | 0 .../{legacy => }/20200930-01-add-backup-run-verb.js | 0 .../20201117-01-remove-deleted-actor-assignments-again.js | 0 .../20201207-01-harmonize-submitter-id-columns.js | 0 .../20210118-01-add-current-flag-to-submission-defs.js | 0 .../migrations/{legacy => }/20210120-01-instance-names.js | 0 .../{legacy => }/20210203-01-add-hierarchy-to-actees.js | 0 .../20210210-01-add-instanceid-to-submission-defs.js | 0 .../{legacy => }/20210218-01-add-submission-edit-verbs.js | 0 .../20210218-02-add-draft-to-submissions-unique.js | 0 .../migrations/{legacy => }/20210219-01-add-review-state.js | 0 .../20210219-02-add-notes-and-index-to-audits.js | 0 .../20210324-01-add-submission-edit-verbs-to-managers.js | 0 .../{legacy => }/20210325-01-remove-project.list-verb.js | 0 .../{legacy => }/20210408-01-drop-public-link-createdat.js | 0 .../20210408-02-backfill-specialized-actor-audits.js | 0 .../migrations/{legacy => }/20210409-01-add-comments.js | 0 .../{legacy => }/20210409-02-update-review-states.js | 0 .../{legacy => }/20210423-01-add-name-to-form-def.js | 0 .../migrations/{legacy => }/20210423-02-drop-form-name.js | 0 .../{legacy => }/20210716-01-config-value-jsonb.js | 0 .../{legacy => }/20210721-01-add-config-set-verb.js | 0 .../20210817-01-disallow-structure-downcast-to-string.js | 0 .../{legacy => }/20210825-01-add-analytics-read-verb.js | 0 .../20210903-01-backfill-encrypted-client-audits.js | 0 .../20210927-01-revert-disallow-structure-downcast.js | 0 .../{legacy => }/20211008-01-track-select-many-options.js | 0 .../{legacy => }/20211021-remove-hashes-from-audits.js | 0 .../20211109-01-add-user-agent-to-submissions.js | 0 .../{legacy => }/20211114-01-flag-initial-submission-def.js | 0 .../{legacy => }/20211117-01-add-form-restore-verb.js | 0 .../20211129-01-add-purged-details-to-actees.js | 0 .../{legacy => }/20220121-01-form-cascade-delete.js | 0 .../{legacy => }/20220121-02-purge-deleted-forms.js | 0 .../{legacy => }/20220209-01-purge-unneeded-drafts.js | 0 .../{legacy => }/20220309-01-add-project-description.js | 0 .../{legacy => }/20220803-01-create-entities-schema.js | 0 .../{legacy => }/20221003-01-add-dataset-verbs.js | 0 .../{legacy => }/20221114-01-explict-dataset-publish.js | 0 ...20221117-01-check-datasetId-is-null-for-non-file-type.js | 0 .../20221118-01-make-entities-columns-not-null.js | 0 .../{legacy => }/20221208-01-reduce-tz-precision.js | 0 .../{legacy => }/20230106-01-remove-revision-number.js | 0 .../migrations/{legacy => }/20230109-01-add-form-schema.js | 0 .../{legacy => }/20230123-01-remove-google-backups.js | 0 .../{legacy => }/20230126-01-add-entity-indices.js | 0 .../{legacy => }/20230127-01-rename-entity-created-by.js | 0 .../{legacy => }/20230324-01-edit-dataset-verbs.js | 0 .../{legacy => }/20230406-01-add-entity-def-fields.js | 0 .../20230406-02-move-entity-label-add-deletedAt.js | 0 .../{legacy => }/20230414-01-remove-user-mfa-secret.js | 0 .../{legacy => }/20230419-01-optimize-indices-sub-defs.js | 0 .../{legacy => }/20230509-01-dataset-approval-flag.js | 0 .../migrations/{legacy => }/20230512-01-add-entity-root.js | 0 .../{legacy => }/20230512-02-backfill-entity-id.js | 0 .../{legacy => }/20230512-03-add-entity-source.js | 0 .../{legacy => }/20230518-01-add-entity-index-to-audits.js | 0 .../{legacy => }/20230802-01-delete-orphan-submissions.js | 0 .../20230818-01-remove-schemaId-from-dsPropertyFields.js | 0 .../{legacy => }/20230824-01-add-entity-version.js | 0 .../20230830-01-remove-entity-label-from-audits.js | 0 .../migrations/{legacy => }/20230907-01-opened-form-verb.js | 0 .../{legacy => }/20231002-01-add-conflict-details.js | 0 .../{legacy => }/20231013-01-change-entity-error-action.js | 0 .../{legacy => }/20231208-01-dataset-form-def-actions.js | 0 .../{legacy => }/20240215-01-entity-delete-verb.js | 0 .../migrations/{legacy => }/20240215-02-dedupe-verbs.js | 0 .../{legacy => }/20240312-01-add-dataset-create-verb.js | 0 .../20240322-01-add-entity-source-index-to-audits.js | 0 .../{legacy => }/20240515-01-entity-tz-precision.js | 0 .../20240607-01-add-offline-entity-branch-trunk-info.js | 0 .../{legacy => }/20240607-02-add-submission-backlog.js | 0 .../20240715-01-backlog-add-event-entityuuid.js | 0 .../migrations/{legacy => }/20240913-01-add-blob-s3.js | 0 .../{legacy => }/20240914-01-add-submission-delete-verb.js | 0 .../20240914-02-remove-orphaned-client-audits.js | 0 .../{legacy => }/20241001-01-index-on-session-table.js | 0 .../{legacy => }/20241008-01-add-user_preferences.js | 0 .../20241010-01-schedule-entity-form-upgrade.js | 0 ...20241029-01-schedule-entity-form-upgrade-create-forms.js | 0 .../{legacy => }/20241030-01-add-force-entity-def-source.js | 0 176 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 lib/model/migrations-post-knex/.eslintrc.json rename lib/model/{migrations => migrations-post-knex}/20250113-01-disable-nullable-blob-content-types.js (100%) rename lib/model/migrations/{legacy => }/20170920-01-initial.js (100%) rename lib/model/migrations/{legacy => }/20171010-01-auth.js (100%) rename lib/model/migrations/{legacy => }/20171023-01-authz-forms.js (100%) rename lib/model/migrations/{legacy => }/20171030-01-add-default-authz-records.js (100%) rename lib/model/migrations/{legacy => }/20171106-01-remove-user-update-timestamp.js (100%) rename lib/model/migrations/{legacy => }/20171121-01-add-submissions-constraint.js (100%) rename lib/model/migrations/{legacy => }/20171121-02-add-submitter.js (100%) rename lib/model/migrations/{legacy => }/20171213-01-unrequire-display-name.js (100%) rename lib/model/migrations/{legacy => }/20180108-01-expiring-actors.js (100%) rename lib/model/migrations/{legacy => }/20180108-02-enum-to-varchar.js (100%) rename lib/model/migrations/{legacy => }/20180112-01-audit-table.js (100%) rename lib/model/migrations/{legacy => }/20180112-02-add-field-keys.js (100%) rename lib/model/migrations/{legacy => }/20180118-01-rerequire-display-name.js (100%) rename lib/model/migrations/{legacy => }/20180125-01-add-form-detail-fields.js (100%) rename lib/model/migrations/{legacy => }/20180125-02-more-field-key-grants.js (100%) rename lib/model/migrations/{legacy => }/20180125-03-add-blob-tables.js (100%) rename lib/model/migrations/{legacy => }/20180301-01-configuration.js (100%) rename lib/model/migrations/{legacy => }/20180322-01-additional-form-options.js (100%) rename lib/model/migrations/{legacy => }/20180327-01-update-form-constraints.js (100%) rename lib/model/migrations/{legacy => }/20180501-01-add-configs-timestamp.js (100%) rename lib/model/migrations/{legacy => }/20180501-02-fix-date-columns.js (100%) rename lib/model/migrations/{legacy => }/20180515-01-enforce-nonnull-form-version.js (100%) rename lib/model/migrations/{legacy => }/20180727-01-rename-attachments-table.js (100%) rename lib/model/migrations/{legacy => }/20180727-02-add-md5-to-blobs.js (100%) rename lib/model/migrations/{legacy => }/20180727-03-add-form-attachments-table.js (100%) rename lib/model/migrations/{legacy => }/20181011-make-email-case-insensitive.js (100%) rename lib/model/migrations/{legacy => }/20181012-01-add-submissions-createdat-index.js (100%) rename lib/model/migrations/{legacy => }/20181206-01-add-projects.js (100%) rename lib/model/migrations/{legacy => }/20181207-01-grant-verbs-to-text.js (100%) rename lib/model/migrations/{legacy => }/20181207-02-rename-grant-verbs.js (100%) rename lib/model/migrations/{legacy => }/20181211-01-audit-verbs-to-text.js (100%) rename lib/model/migrations/{legacy => }/20181211-02-rename-audit-actions.js (100%) rename lib/model/migrations/{legacy => }/20181212-00-fix-user-type.js (100%) rename lib/model/migrations/{legacy => }/20181212-01-add-roles.js (100%) rename lib/model/migrations/{legacy => }/20181212-02-remove-groups.js (100%) rename lib/model/migrations/{legacy => }/20181212-03-add-single-use-roles.js (100%) rename lib/model/migrations/{legacy => }/20181219-01-add-submission-update-verb.js (100%) rename lib/model/migrations/{legacy => }/20181221-01-nullable-submission-blobs.js (100%) rename lib/model/migrations/{legacy => }/20181230-01-add-device-id-to-submission.js (100%) rename lib/model/migrations/{legacy => }/20190225-01-add-actor-trigram-indices.js (100%) rename lib/model/migrations/{legacy => }/20190225-02-add-role-grants.js (100%) rename lib/model/migrations/{legacy => }/20190226-01-convert-verbs-to-jsonb.js (100%) rename lib/model/migrations/{legacy => }/20190226-02-add-role-actee-species.js (100%) rename lib/model/migrations/{legacy => }/20190226-03-add-assignment-verbs.js (100%) rename lib/model/migrations/{legacy => }/20190226-04-add-assignment-actee-species.js (100%) rename lib/model/migrations/{legacy => }/20190227-01-add-project-manager-role.js (100%) rename lib/model/migrations/{legacy => }/20190405-01-add-project-archival-flag.js (100%) rename lib/model/migrations/{legacy => }/20190416-01-email-uniqueness.js (100%) rename lib/model/migrations/{legacy => }/20190416-02-add-user-delete-verb.js (100%) rename lib/model/migrations/{legacy => }/20190520-01-add-form-versioning.js (100%) rename lib/model/migrations/{legacy => }/20190523-01-add-form-state-constraint.js (100%) rename lib/model/migrations/{legacy => }/20190605-01-reformat-audits.js (100%) rename lib/model/migrations/{legacy => }/20190607-01-convert-audit-details-to-jsonb.js (100%) rename lib/model/migrations/{legacy => }/20190607-02-standardize-attachment-actees.js (100%) rename lib/model/migrations/{legacy => }/20190607-03-rename-sub-attachment-audits.js (100%) rename lib/model/migrations/{legacy => }/20190610-01-add-audits-verbs.js (100%) rename lib/model/migrations/{legacy => }/20190610-02-backfill-submission-audit-instanceids.js (100%) rename lib/model/migrations/{legacy => }/20190611-01-add-updatedat-to-form-attachments.js (100%) rename lib/model/migrations/{legacy => }/20190618-01-add-csrf-token.js (100%) rename lib/model/migrations/{legacy => }/20190618-01-add-encryption-tracking.js (100%) rename lib/model/migrations/{legacy => }/20190701-01-add-managed-encryption-key-check.js (100%) rename lib/model/migrations/{legacy => }/20190916-01-granularize-app-user-permissions.js (100%) rename lib/model/migrations/{legacy => }/20190917-01-cleanup-app-user-role.js (100%) rename lib/model/migrations/{legacy => }/20190923-01-add-project-viewer-role.js (100%) rename lib/model/migrations/{legacy => }/20190925-01-add-client-audits.js (100%) rename lib/model/migrations/{legacy => }/20191007-01-backfill-client-audits.js (100%) rename lib/model/migrations/{legacy => }/20191010-01-add-excel-blob-reference.js (100%) rename lib/model/migrations/{legacy => }/20191023-01-add-worker-columns-to-audits.js (100%) rename lib/model/migrations/{legacy => }/20191025-01-add-id-to-audits.js (100%) rename lib/model/migrations/{legacy => }/20191106-01-remove-deleted-actor-assignments.js (100%) rename lib/model/migrations/{legacy => }/20191231-01-remove-transformations.js (100%) rename lib/model/migrations/{legacy => }/20191231-02-add-schema-storage.js (100%) rename lib/model/migrations/{legacy => }/20200110-01-add-drafts.js (100%) rename lib/model/migrations/{legacy => }/20200112-01-check-field-collisions.js (100%) rename lib/model/migrations/{legacy => }/20200114-01-remove-formid-sha256-constraint.js (100%) rename lib/model/migrations/{legacy => }/20200117-01-draft-test-submissions.js (100%) rename lib/model/migrations/{legacy => }/20200121-01-add-draft-keys.js (100%) rename lib/model/migrations/{legacy => }/20200122-01-remove-draft-form-state.js (100%) rename lib/model/migrations/{legacy => }/20200129-01-cascade-submission-deletes.js (100%) rename lib/model/migrations/{legacy => }/20200220-01-repair-submission-parsing.js (100%) rename lib/model/migrations/{legacy => }/20200403-01-add-performance-indices.js (100%) rename lib/model/migrations/{legacy => }/20200407-01-allow-actorless-submission-defs.js (100%) rename lib/model/migrations/{legacy => }/20200423-01-fix-field-insert-performance.js (100%) rename lib/model/migrations/{legacy => }/20200428-01-allow-string-downcast.js (100%) rename lib/model/migrations/{legacy => }/20200519-01-add-enketo-id.js (100%) rename lib/model/migrations/{legacy => }/20200519-02-add-form-viewer-role.js (100%) rename lib/model/migrations/{legacy => }/20200520-01-backfill-enketo.js (100%) rename lib/model/migrations/{legacy => }/20200715-01-add-data-collector-role.js (100%) rename lib/model/migrations/{legacy => }/20200721-01-add-public-links.js (100%) rename lib/model/migrations/{legacy => }/20200728-01-add-enketo-single-token-to-forms.js (100%) rename lib/model/migrations/{legacy => }/20200731-01-allow-project-managers-to-end-sessions.js (100%) rename lib/model/migrations/{legacy => }/20200810-01-reschedule-enketo-processing.js (100%) rename lib/model/migrations/{legacy => }/20200918-01-repair-publishedat-dates.js (100%) rename lib/model/migrations/{legacy => }/20200930-01-add-backup-run-verb.js (100%) rename lib/model/migrations/{legacy => }/20201117-01-remove-deleted-actor-assignments-again.js (100%) rename lib/model/migrations/{legacy => }/20201207-01-harmonize-submitter-id-columns.js (100%) rename lib/model/migrations/{legacy => }/20210118-01-add-current-flag-to-submission-defs.js (100%) rename lib/model/migrations/{legacy => }/20210120-01-instance-names.js (100%) rename lib/model/migrations/{legacy => }/20210203-01-add-hierarchy-to-actees.js (100%) rename lib/model/migrations/{legacy => }/20210210-01-add-instanceid-to-submission-defs.js (100%) rename lib/model/migrations/{legacy => }/20210218-01-add-submission-edit-verbs.js (100%) rename lib/model/migrations/{legacy => }/20210218-02-add-draft-to-submissions-unique.js (100%) rename lib/model/migrations/{legacy => }/20210219-01-add-review-state.js (100%) rename lib/model/migrations/{legacy => }/20210219-02-add-notes-and-index-to-audits.js (100%) rename lib/model/migrations/{legacy => }/20210324-01-add-submission-edit-verbs-to-managers.js (100%) rename lib/model/migrations/{legacy => }/20210325-01-remove-project.list-verb.js (100%) rename lib/model/migrations/{legacy => }/20210408-01-drop-public-link-createdat.js (100%) rename lib/model/migrations/{legacy => }/20210408-02-backfill-specialized-actor-audits.js (100%) rename lib/model/migrations/{legacy => }/20210409-01-add-comments.js (100%) rename lib/model/migrations/{legacy => }/20210409-02-update-review-states.js (100%) rename lib/model/migrations/{legacy => }/20210423-01-add-name-to-form-def.js (100%) rename lib/model/migrations/{legacy => }/20210423-02-drop-form-name.js (100%) rename lib/model/migrations/{legacy => }/20210716-01-config-value-jsonb.js (100%) rename lib/model/migrations/{legacy => }/20210721-01-add-config-set-verb.js (100%) rename lib/model/migrations/{legacy => }/20210817-01-disallow-structure-downcast-to-string.js (100%) rename lib/model/migrations/{legacy => }/20210825-01-add-analytics-read-verb.js (100%) rename lib/model/migrations/{legacy => }/20210903-01-backfill-encrypted-client-audits.js (100%) rename lib/model/migrations/{legacy => }/20210927-01-revert-disallow-structure-downcast.js (100%) rename lib/model/migrations/{legacy => }/20211008-01-track-select-many-options.js (100%) rename lib/model/migrations/{legacy => }/20211021-remove-hashes-from-audits.js (100%) rename lib/model/migrations/{legacy => }/20211109-01-add-user-agent-to-submissions.js (100%) rename lib/model/migrations/{legacy => }/20211114-01-flag-initial-submission-def.js (100%) rename lib/model/migrations/{legacy => }/20211117-01-add-form-restore-verb.js (100%) rename lib/model/migrations/{legacy => }/20211129-01-add-purged-details-to-actees.js (100%) rename lib/model/migrations/{legacy => }/20220121-01-form-cascade-delete.js (100%) rename lib/model/migrations/{legacy => }/20220121-02-purge-deleted-forms.js (100%) rename lib/model/migrations/{legacy => }/20220209-01-purge-unneeded-drafts.js (100%) rename lib/model/migrations/{legacy => }/20220309-01-add-project-description.js (100%) rename lib/model/migrations/{legacy => }/20220803-01-create-entities-schema.js (100%) rename lib/model/migrations/{legacy => }/20221003-01-add-dataset-verbs.js (100%) rename lib/model/migrations/{legacy => }/20221114-01-explict-dataset-publish.js (100%) rename lib/model/migrations/{legacy => }/20221117-01-check-datasetId-is-null-for-non-file-type.js (100%) rename lib/model/migrations/{legacy => }/20221118-01-make-entities-columns-not-null.js (100%) rename lib/model/migrations/{legacy => }/20221208-01-reduce-tz-precision.js (100%) rename lib/model/migrations/{legacy => }/20230106-01-remove-revision-number.js (100%) rename lib/model/migrations/{legacy => }/20230109-01-add-form-schema.js (100%) rename lib/model/migrations/{legacy => }/20230123-01-remove-google-backups.js (100%) rename lib/model/migrations/{legacy => }/20230126-01-add-entity-indices.js (100%) rename lib/model/migrations/{legacy => }/20230127-01-rename-entity-created-by.js (100%) rename lib/model/migrations/{legacy => }/20230324-01-edit-dataset-verbs.js (100%) rename lib/model/migrations/{legacy => }/20230406-01-add-entity-def-fields.js (100%) rename lib/model/migrations/{legacy => }/20230406-02-move-entity-label-add-deletedAt.js (100%) rename lib/model/migrations/{legacy => }/20230414-01-remove-user-mfa-secret.js (100%) rename lib/model/migrations/{legacy => }/20230419-01-optimize-indices-sub-defs.js (100%) rename lib/model/migrations/{legacy => }/20230509-01-dataset-approval-flag.js (100%) rename lib/model/migrations/{legacy => }/20230512-01-add-entity-root.js (100%) rename lib/model/migrations/{legacy => }/20230512-02-backfill-entity-id.js (100%) rename lib/model/migrations/{legacy => }/20230512-03-add-entity-source.js (100%) rename lib/model/migrations/{legacy => }/20230518-01-add-entity-index-to-audits.js (100%) rename lib/model/migrations/{legacy => }/20230802-01-delete-orphan-submissions.js (100%) rename lib/model/migrations/{legacy => }/20230818-01-remove-schemaId-from-dsPropertyFields.js (100%) rename lib/model/migrations/{legacy => }/20230824-01-add-entity-version.js (100%) rename lib/model/migrations/{legacy => }/20230830-01-remove-entity-label-from-audits.js (100%) rename lib/model/migrations/{legacy => }/20230907-01-opened-form-verb.js (100%) rename lib/model/migrations/{legacy => }/20231002-01-add-conflict-details.js (100%) rename lib/model/migrations/{legacy => }/20231013-01-change-entity-error-action.js (100%) rename lib/model/migrations/{legacy => }/20231208-01-dataset-form-def-actions.js (100%) rename lib/model/migrations/{legacy => }/20240215-01-entity-delete-verb.js (100%) rename lib/model/migrations/{legacy => }/20240215-02-dedupe-verbs.js (100%) rename lib/model/migrations/{legacy => }/20240312-01-add-dataset-create-verb.js (100%) rename lib/model/migrations/{legacy => }/20240322-01-add-entity-source-index-to-audits.js (100%) rename lib/model/migrations/{legacy => }/20240515-01-entity-tz-precision.js (100%) rename lib/model/migrations/{legacy => }/20240607-01-add-offline-entity-branch-trunk-info.js (100%) rename lib/model/migrations/{legacy => }/20240607-02-add-submission-backlog.js (100%) rename lib/model/migrations/{legacy => }/20240715-01-backlog-add-event-entityuuid.js (100%) rename lib/model/migrations/{legacy => }/20240913-01-add-blob-s3.js (100%) rename lib/model/migrations/{legacy => }/20240914-01-add-submission-delete-verb.js (100%) rename lib/model/migrations/{legacy => }/20240914-02-remove-orphaned-client-audits.js (100%) rename lib/model/migrations/{legacy => }/20241001-01-index-on-session-table.js (100%) rename lib/model/migrations/{legacy => }/20241008-01-add-user_preferences.js (100%) rename lib/model/migrations/{legacy => }/20241010-01-schedule-entity-form-upgrade.js (100%) rename lib/model/migrations/{legacy => }/20241029-01-schedule-entity-form-upgrade-create-forms.js (100%) rename lib/model/migrations/{legacy => }/20241030-01-add-force-entity-def-source.js (100%) diff --git a/lib/model/migrate.js b/lib/model/migrate.js index 63d48eb10..6e25cfa3b 100644 --- a/lib/model/migrate.js +++ b/lib/model/migrate.js @@ -20,8 +20,8 @@ const { knexConnection } = require('../util/db'); // Connects to the postgres database specified in configuration and returns it. const initKnex = (config) => knex({ client: 'pg', connection: knexConnection(config) }); -const legacyPath = `${__dirname}/migrations/legacy`; -const postKnexPath = `${__dirname}/migrations`; +const legacyPath = `${__dirname}/migrations`; +const postKnexPath = `${__dirname}/migrations-post-knex`; // Connects to a database, passes it to a function for operations, then ensures its closure. const withKnex = (config) => (mutator) => { diff --git a/lib/model/migrations-post-knex/.eslintrc.json b/lib/model/migrations-post-knex/.eslintrc.json new file mode 100644 index 000000000..93a99d9dd --- /dev/null +++ b/lib/model/migrations-post-knex/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "../../../.eslintrc.json", + "rules": { + "no-restricted-modules": [ "error", { "patterns": [ "../*" ] } ] + } +} diff --git a/lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js b/lib/model/migrations-post-knex/20250113-01-disable-nullable-blob-content-types.js similarity index 100% rename from lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js rename to lib/model/migrations-post-knex/20250113-01-disable-nullable-blob-content-types.js diff --git a/lib/model/migrations/legacy/20170920-01-initial.js b/lib/model/migrations/20170920-01-initial.js similarity index 100% rename from lib/model/migrations/legacy/20170920-01-initial.js rename to lib/model/migrations/20170920-01-initial.js diff --git a/lib/model/migrations/legacy/20171010-01-auth.js b/lib/model/migrations/20171010-01-auth.js similarity index 100% rename from lib/model/migrations/legacy/20171010-01-auth.js rename to lib/model/migrations/20171010-01-auth.js diff --git a/lib/model/migrations/legacy/20171023-01-authz-forms.js b/lib/model/migrations/20171023-01-authz-forms.js similarity index 100% rename from lib/model/migrations/legacy/20171023-01-authz-forms.js rename to lib/model/migrations/20171023-01-authz-forms.js diff --git a/lib/model/migrations/legacy/20171030-01-add-default-authz-records.js b/lib/model/migrations/20171030-01-add-default-authz-records.js similarity index 100% rename from lib/model/migrations/legacy/20171030-01-add-default-authz-records.js rename to lib/model/migrations/20171030-01-add-default-authz-records.js diff --git a/lib/model/migrations/legacy/20171106-01-remove-user-update-timestamp.js b/lib/model/migrations/20171106-01-remove-user-update-timestamp.js similarity index 100% rename from lib/model/migrations/legacy/20171106-01-remove-user-update-timestamp.js rename to lib/model/migrations/20171106-01-remove-user-update-timestamp.js diff --git a/lib/model/migrations/legacy/20171121-01-add-submissions-constraint.js b/lib/model/migrations/20171121-01-add-submissions-constraint.js similarity index 100% rename from lib/model/migrations/legacy/20171121-01-add-submissions-constraint.js rename to lib/model/migrations/20171121-01-add-submissions-constraint.js diff --git a/lib/model/migrations/legacy/20171121-02-add-submitter.js b/lib/model/migrations/20171121-02-add-submitter.js similarity index 100% rename from lib/model/migrations/legacy/20171121-02-add-submitter.js rename to lib/model/migrations/20171121-02-add-submitter.js diff --git a/lib/model/migrations/legacy/20171213-01-unrequire-display-name.js b/lib/model/migrations/20171213-01-unrequire-display-name.js similarity index 100% rename from lib/model/migrations/legacy/20171213-01-unrequire-display-name.js rename to lib/model/migrations/20171213-01-unrequire-display-name.js diff --git a/lib/model/migrations/legacy/20180108-01-expiring-actors.js b/lib/model/migrations/20180108-01-expiring-actors.js similarity index 100% rename from lib/model/migrations/legacy/20180108-01-expiring-actors.js rename to lib/model/migrations/20180108-01-expiring-actors.js diff --git a/lib/model/migrations/legacy/20180108-02-enum-to-varchar.js b/lib/model/migrations/20180108-02-enum-to-varchar.js similarity index 100% rename from lib/model/migrations/legacy/20180108-02-enum-to-varchar.js rename to lib/model/migrations/20180108-02-enum-to-varchar.js diff --git a/lib/model/migrations/legacy/20180112-01-audit-table.js b/lib/model/migrations/20180112-01-audit-table.js similarity index 100% rename from lib/model/migrations/legacy/20180112-01-audit-table.js rename to lib/model/migrations/20180112-01-audit-table.js diff --git a/lib/model/migrations/legacy/20180112-02-add-field-keys.js b/lib/model/migrations/20180112-02-add-field-keys.js similarity index 100% rename from lib/model/migrations/legacy/20180112-02-add-field-keys.js rename to lib/model/migrations/20180112-02-add-field-keys.js diff --git a/lib/model/migrations/legacy/20180118-01-rerequire-display-name.js b/lib/model/migrations/20180118-01-rerequire-display-name.js similarity index 100% rename from lib/model/migrations/legacy/20180118-01-rerequire-display-name.js rename to lib/model/migrations/20180118-01-rerequire-display-name.js diff --git a/lib/model/migrations/legacy/20180125-01-add-form-detail-fields.js b/lib/model/migrations/20180125-01-add-form-detail-fields.js similarity index 100% rename from lib/model/migrations/legacy/20180125-01-add-form-detail-fields.js rename to lib/model/migrations/20180125-01-add-form-detail-fields.js diff --git a/lib/model/migrations/legacy/20180125-02-more-field-key-grants.js b/lib/model/migrations/20180125-02-more-field-key-grants.js similarity index 100% rename from lib/model/migrations/legacy/20180125-02-more-field-key-grants.js rename to lib/model/migrations/20180125-02-more-field-key-grants.js diff --git a/lib/model/migrations/legacy/20180125-03-add-blob-tables.js b/lib/model/migrations/20180125-03-add-blob-tables.js similarity index 100% rename from lib/model/migrations/legacy/20180125-03-add-blob-tables.js rename to lib/model/migrations/20180125-03-add-blob-tables.js diff --git a/lib/model/migrations/legacy/20180301-01-configuration.js b/lib/model/migrations/20180301-01-configuration.js similarity index 100% rename from lib/model/migrations/legacy/20180301-01-configuration.js rename to lib/model/migrations/20180301-01-configuration.js diff --git a/lib/model/migrations/legacy/20180322-01-additional-form-options.js b/lib/model/migrations/20180322-01-additional-form-options.js similarity index 100% rename from lib/model/migrations/legacy/20180322-01-additional-form-options.js rename to lib/model/migrations/20180322-01-additional-form-options.js diff --git a/lib/model/migrations/legacy/20180327-01-update-form-constraints.js b/lib/model/migrations/20180327-01-update-form-constraints.js similarity index 100% rename from lib/model/migrations/legacy/20180327-01-update-form-constraints.js rename to lib/model/migrations/20180327-01-update-form-constraints.js diff --git a/lib/model/migrations/legacy/20180501-01-add-configs-timestamp.js b/lib/model/migrations/20180501-01-add-configs-timestamp.js similarity index 100% rename from lib/model/migrations/legacy/20180501-01-add-configs-timestamp.js rename to lib/model/migrations/20180501-01-add-configs-timestamp.js diff --git a/lib/model/migrations/legacy/20180501-02-fix-date-columns.js b/lib/model/migrations/20180501-02-fix-date-columns.js similarity index 100% rename from lib/model/migrations/legacy/20180501-02-fix-date-columns.js rename to lib/model/migrations/20180501-02-fix-date-columns.js diff --git a/lib/model/migrations/legacy/20180515-01-enforce-nonnull-form-version.js b/lib/model/migrations/20180515-01-enforce-nonnull-form-version.js similarity index 100% rename from lib/model/migrations/legacy/20180515-01-enforce-nonnull-form-version.js rename to lib/model/migrations/20180515-01-enforce-nonnull-form-version.js diff --git a/lib/model/migrations/legacy/20180727-01-rename-attachments-table.js b/lib/model/migrations/20180727-01-rename-attachments-table.js similarity index 100% rename from lib/model/migrations/legacy/20180727-01-rename-attachments-table.js rename to lib/model/migrations/20180727-01-rename-attachments-table.js diff --git a/lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js b/lib/model/migrations/20180727-02-add-md5-to-blobs.js similarity index 100% rename from lib/model/migrations/legacy/20180727-02-add-md5-to-blobs.js rename to lib/model/migrations/20180727-02-add-md5-to-blobs.js diff --git a/lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js b/lib/model/migrations/20180727-03-add-form-attachments-table.js similarity index 100% rename from lib/model/migrations/legacy/20180727-03-add-form-attachments-table.js rename to lib/model/migrations/20180727-03-add-form-attachments-table.js diff --git a/lib/model/migrations/legacy/20181011-make-email-case-insensitive.js b/lib/model/migrations/20181011-make-email-case-insensitive.js similarity index 100% rename from lib/model/migrations/legacy/20181011-make-email-case-insensitive.js rename to lib/model/migrations/20181011-make-email-case-insensitive.js diff --git a/lib/model/migrations/legacy/20181012-01-add-submissions-createdat-index.js b/lib/model/migrations/20181012-01-add-submissions-createdat-index.js similarity index 100% rename from lib/model/migrations/legacy/20181012-01-add-submissions-createdat-index.js rename to lib/model/migrations/20181012-01-add-submissions-createdat-index.js diff --git a/lib/model/migrations/legacy/20181206-01-add-projects.js b/lib/model/migrations/20181206-01-add-projects.js similarity index 100% rename from lib/model/migrations/legacy/20181206-01-add-projects.js rename to lib/model/migrations/20181206-01-add-projects.js diff --git a/lib/model/migrations/legacy/20181207-01-grant-verbs-to-text.js b/lib/model/migrations/20181207-01-grant-verbs-to-text.js similarity index 100% rename from lib/model/migrations/legacy/20181207-01-grant-verbs-to-text.js rename to lib/model/migrations/20181207-01-grant-verbs-to-text.js diff --git a/lib/model/migrations/legacy/20181207-02-rename-grant-verbs.js b/lib/model/migrations/20181207-02-rename-grant-verbs.js similarity index 100% rename from lib/model/migrations/legacy/20181207-02-rename-grant-verbs.js rename to lib/model/migrations/20181207-02-rename-grant-verbs.js diff --git a/lib/model/migrations/legacy/20181211-01-audit-verbs-to-text.js b/lib/model/migrations/20181211-01-audit-verbs-to-text.js similarity index 100% rename from lib/model/migrations/legacy/20181211-01-audit-verbs-to-text.js rename to lib/model/migrations/20181211-01-audit-verbs-to-text.js diff --git a/lib/model/migrations/legacy/20181211-02-rename-audit-actions.js b/lib/model/migrations/20181211-02-rename-audit-actions.js similarity index 100% rename from lib/model/migrations/legacy/20181211-02-rename-audit-actions.js rename to lib/model/migrations/20181211-02-rename-audit-actions.js diff --git a/lib/model/migrations/legacy/20181212-00-fix-user-type.js b/lib/model/migrations/20181212-00-fix-user-type.js similarity index 100% rename from lib/model/migrations/legacy/20181212-00-fix-user-type.js rename to lib/model/migrations/20181212-00-fix-user-type.js diff --git a/lib/model/migrations/legacy/20181212-01-add-roles.js b/lib/model/migrations/20181212-01-add-roles.js similarity index 100% rename from lib/model/migrations/legacy/20181212-01-add-roles.js rename to lib/model/migrations/20181212-01-add-roles.js diff --git a/lib/model/migrations/legacy/20181212-02-remove-groups.js b/lib/model/migrations/20181212-02-remove-groups.js similarity index 100% rename from lib/model/migrations/legacy/20181212-02-remove-groups.js rename to lib/model/migrations/20181212-02-remove-groups.js diff --git a/lib/model/migrations/legacy/20181212-03-add-single-use-roles.js b/lib/model/migrations/20181212-03-add-single-use-roles.js similarity index 100% rename from lib/model/migrations/legacy/20181212-03-add-single-use-roles.js rename to lib/model/migrations/20181212-03-add-single-use-roles.js diff --git a/lib/model/migrations/legacy/20181219-01-add-submission-update-verb.js b/lib/model/migrations/20181219-01-add-submission-update-verb.js similarity index 100% rename from lib/model/migrations/legacy/20181219-01-add-submission-update-verb.js rename to lib/model/migrations/20181219-01-add-submission-update-verb.js diff --git a/lib/model/migrations/legacy/20181221-01-nullable-submission-blobs.js b/lib/model/migrations/20181221-01-nullable-submission-blobs.js similarity index 100% rename from lib/model/migrations/legacy/20181221-01-nullable-submission-blobs.js rename to lib/model/migrations/20181221-01-nullable-submission-blobs.js diff --git a/lib/model/migrations/legacy/20181230-01-add-device-id-to-submission.js b/lib/model/migrations/20181230-01-add-device-id-to-submission.js similarity index 100% rename from lib/model/migrations/legacy/20181230-01-add-device-id-to-submission.js rename to lib/model/migrations/20181230-01-add-device-id-to-submission.js diff --git a/lib/model/migrations/legacy/20190225-01-add-actor-trigram-indices.js b/lib/model/migrations/20190225-01-add-actor-trigram-indices.js similarity index 100% rename from lib/model/migrations/legacy/20190225-01-add-actor-trigram-indices.js rename to lib/model/migrations/20190225-01-add-actor-trigram-indices.js diff --git a/lib/model/migrations/legacy/20190225-02-add-role-grants.js b/lib/model/migrations/20190225-02-add-role-grants.js similarity index 100% rename from lib/model/migrations/legacy/20190225-02-add-role-grants.js rename to lib/model/migrations/20190225-02-add-role-grants.js diff --git a/lib/model/migrations/legacy/20190226-01-convert-verbs-to-jsonb.js b/lib/model/migrations/20190226-01-convert-verbs-to-jsonb.js similarity index 100% rename from lib/model/migrations/legacy/20190226-01-convert-verbs-to-jsonb.js rename to lib/model/migrations/20190226-01-convert-verbs-to-jsonb.js diff --git a/lib/model/migrations/legacy/20190226-02-add-role-actee-species.js b/lib/model/migrations/20190226-02-add-role-actee-species.js similarity index 100% rename from lib/model/migrations/legacy/20190226-02-add-role-actee-species.js rename to lib/model/migrations/20190226-02-add-role-actee-species.js diff --git a/lib/model/migrations/legacy/20190226-03-add-assignment-verbs.js b/lib/model/migrations/20190226-03-add-assignment-verbs.js similarity index 100% rename from lib/model/migrations/legacy/20190226-03-add-assignment-verbs.js rename to lib/model/migrations/20190226-03-add-assignment-verbs.js diff --git a/lib/model/migrations/legacy/20190226-04-add-assignment-actee-species.js b/lib/model/migrations/20190226-04-add-assignment-actee-species.js similarity index 100% rename from lib/model/migrations/legacy/20190226-04-add-assignment-actee-species.js rename to lib/model/migrations/20190226-04-add-assignment-actee-species.js diff --git a/lib/model/migrations/legacy/20190227-01-add-project-manager-role.js b/lib/model/migrations/20190227-01-add-project-manager-role.js similarity index 100% rename from lib/model/migrations/legacy/20190227-01-add-project-manager-role.js rename to lib/model/migrations/20190227-01-add-project-manager-role.js diff --git a/lib/model/migrations/legacy/20190405-01-add-project-archival-flag.js b/lib/model/migrations/20190405-01-add-project-archival-flag.js similarity index 100% rename from lib/model/migrations/legacy/20190405-01-add-project-archival-flag.js rename to lib/model/migrations/20190405-01-add-project-archival-flag.js diff --git a/lib/model/migrations/legacy/20190416-01-email-uniqueness.js b/lib/model/migrations/20190416-01-email-uniqueness.js similarity index 100% rename from lib/model/migrations/legacy/20190416-01-email-uniqueness.js rename to lib/model/migrations/20190416-01-email-uniqueness.js diff --git a/lib/model/migrations/legacy/20190416-02-add-user-delete-verb.js b/lib/model/migrations/20190416-02-add-user-delete-verb.js similarity index 100% rename from lib/model/migrations/legacy/20190416-02-add-user-delete-verb.js rename to lib/model/migrations/20190416-02-add-user-delete-verb.js diff --git a/lib/model/migrations/legacy/20190520-01-add-form-versioning.js b/lib/model/migrations/20190520-01-add-form-versioning.js similarity index 100% rename from lib/model/migrations/legacy/20190520-01-add-form-versioning.js rename to lib/model/migrations/20190520-01-add-form-versioning.js diff --git a/lib/model/migrations/legacy/20190523-01-add-form-state-constraint.js b/lib/model/migrations/20190523-01-add-form-state-constraint.js similarity index 100% rename from lib/model/migrations/legacy/20190523-01-add-form-state-constraint.js rename to lib/model/migrations/20190523-01-add-form-state-constraint.js diff --git a/lib/model/migrations/legacy/20190605-01-reformat-audits.js b/lib/model/migrations/20190605-01-reformat-audits.js similarity index 100% rename from lib/model/migrations/legacy/20190605-01-reformat-audits.js rename to lib/model/migrations/20190605-01-reformat-audits.js diff --git a/lib/model/migrations/legacy/20190607-01-convert-audit-details-to-jsonb.js b/lib/model/migrations/20190607-01-convert-audit-details-to-jsonb.js similarity index 100% rename from lib/model/migrations/legacy/20190607-01-convert-audit-details-to-jsonb.js rename to lib/model/migrations/20190607-01-convert-audit-details-to-jsonb.js diff --git a/lib/model/migrations/legacy/20190607-02-standardize-attachment-actees.js b/lib/model/migrations/20190607-02-standardize-attachment-actees.js similarity index 100% rename from lib/model/migrations/legacy/20190607-02-standardize-attachment-actees.js rename to lib/model/migrations/20190607-02-standardize-attachment-actees.js diff --git a/lib/model/migrations/legacy/20190607-03-rename-sub-attachment-audits.js b/lib/model/migrations/20190607-03-rename-sub-attachment-audits.js similarity index 100% rename from lib/model/migrations/legacy/20190607-03-rename-sub-attachment-audits.js rename to lib/model/migrations/20190607-03-rename-sub-attachment-audits.js diff --git a/lib/model/migrations/legacy/20190610-01-add-audits-verbs.js b/lib/model/migrations/20190610-01-add-audits-verbs.js similarity index 100% rename from lib/model/migrations/legacy/20190610-01-add-audits-verbs.js rename to lib/model/migrations/20190610-01-add-audits-verbs.js diff --git a/lib/model/migrations/legacy/20190610-02-backfill-submission-audit-instanceids.js b/lib/model/migrations/20190610-02-backfill-submission-audit-instanceids.js similarity index 100% rename from lib/model/migrations/legacy/20190610-02-backfill-submission-audit-instanceids.js rename to lib/model/migrations/20190610-02-backfill-submission-audit-instanceids.js diff --git a/lib/model/migrations/legacy/20190611-01-add-updatedat-to-form-attachments.js b/lib/model/migrations/20190611-01-add-updatedat-to-form-attachments.js similarity index 100% rename from lib/model/migrations/legacy/20190611-01-add-updatedat-to-form-attachments.js rename to lib/model/migrations/20190611-01-add-updatedat-to-form-attachments.js diff --git a/lib/model/migrations/legacy/20190618-01-add-csrf-token.js b/lib/model/migrations/20190618-01-add-csrf-token.js similarity index 100% rename from lib/model/migrations/legacy/20190618-01-add-csrf-token.js rename to lib/model/migrations/20190618-01-add-csrf-token.js diff --git a/lib/model/migrations/legacy/20190618-01-add-encryption-tracking.js b/lib/model/migrations/20190618-01-add-encryption-tracking.js similarity index 100% rename from lib/model/migrations/legacy/20190618-01-add-encryption-tracking.js rename to lib/model/migrations/20190618-01-add-encryption-tracking.js diff --git a/lib/model/migrations/legacy/20190701-01-add-managed-encryption-key-check.js b/lib/model/migrations/20190701-01-add-managed-encryption-key-check.js similarity index 100% rename from lib/model/migrations/legacy/20190701-01-add-managed-encryption-key-check.js rename to lib/model/migrations/20190701-01-add-managed-encryption-key-check.js diff --git a/lib/model/migrations/legacy/20190916-01-granularize-app-user-permissions.js b/lib/model/migrations/20190916-01-granularize-app-user-permissions.js similarity index 100% rename from lib/model/migrations/legacy/20190916-01-granularize-app-user-permissions.js rename to lib/model/migrations/20190916-01-granularize-app-user-permissions.js diff --git a/lib/model/migrations/legacy/20190917-01-cleanup-app-user-role.js b/lib/model/migrations/20190917-01-cleanup-app-user-role.js similarity index 100% rename from lib/model/migrations/legacy/20190917-01-cleanup-app-user-role.js rename to lib/model/migrations/20190917-01-cleanup-app-user-role.js diff --git a/lib/model/migrations/legacy/20190923-01-add-project-viewer-role.js b/lib/model/migrations/20190923-01-add-project-viewer-role.js similarity index 100% rename from lib/model/migrations/legacy/20190923-01-add-project-viewer-role.js rename to lib/model/migrations/20190923-01-add-project-viewer-role.js diff --git a/lib/model/migrations/legacy/20190925-01-add-client-audits.js b/lib/model/migrations/20190925-01-add-client-audits.js similarity index 100% rename from lib/model/migrations/legacy/20190925-01-add-client-audits.js rename to lib/model/migrations/20190925-01-add-client-audits.js diff --git a/lib/model/migrations/legacy/20191007-01-backfill-client-audits.js b/lib/model/migrations/20191007-01-backfill-client-audits.js similarity index 100% rename from lib/model/migrations/legacy/20191007-01-backfill-client-audits.js rename to lib/model/migrations/20191007-01-backfill-client-audits.js diff --git a/lib/model/migrations/legacy/20191010-01-add-excel-blob-reference.js b/lib/model/migrations/20191010-01-add-excel-blob-reference.js similarity index 100% rename from lib/model/migrations/legacy/20191010-01-add-excel-blob-reference.js rename to lib/model/migrations/20191010-01-add-excel-blob-reference.js diff --git a/lib/model/migrations/legacy/20191023-01-add-worker-columns-to-audits.js b/lib/model/migrations/20191023-01-add-worker-columns-to-audits.js similarity index 100% rename from lib/model/migrations/legacy/20191023-01-add-worker-columns-to-audits.js rename to lib/model/migrations/20191023-01-add-worker-columns-to-audits.js diff --git a/lib/model/migrations/legacy/20191025-01-add-id-to-audits.js b/lib/model/migrations/20191025-01-add-id-to-audits.js similarity index 100% rename from lib/model/migrations/legacy/20191025-01-add-id-to-audits.js rename to lib/model/migrations/20191025-01-add-id-to-audits.js diff --git a/lib/model/migrations/legacy/20191106-01-remove-deleted-actor-assignments.js b/lib/model/migrations/20191106-01-remove-deleted-actor-assignments.js similarity index 100% rename from lib/model/migrations/legacy/20191106-01-remove-deleted-actor-assignments.js rename to lib/model/migrations/20191106-01-remove-deleted-actor-assignments.js diff --git a/lib/model/migrations/legacy/20191231-01-remove-transformations.js b/lib/model/migrations/20191231-01-remove-transformations.js similarity index 100% rename from lib/model/migrations/legacy/20191231-01-remove-transformations.js rename to lib/model/migrations/20191231-01-remove-transformations.js diff --git a/lib/model/migrations/legacy/20191231-02-add-schema-storage.js b/lib/model/migrations/20191231-02-add-schema-storage.js similarity index 100% rename from lib/model/migrations/legacy/20191231-02-add-schema-storage.js rename to lib/model/migrations/20191231-02-add-schema-storage.js diff --git a/lib/model/migrations/legacy/20200110-01-add-drafts.js b/lib/model/migrations/20200110-01-add-drafts.js similarity index 100% rename from lib/model/migrations/legacy/20200110-01-add-drafts.js rename to lib/model/migrations/20200110-01-add-drafts.js diff --git a/lib/model/migrations/legacy/20200112-01-check-field-collisions.js b/lib/model/migrations/20200112-01-check-field-collisions.js similarity index 100% rename from lib/model/migrations/legacy/20200112-01-check-field-collisions.js rename to lib/model/migrations/20200112-01-check-field-collisions.js diff --git a/lib/model/migrations/legacy/20200114-01-remove-formid-sha256-constraint.js b/lib/model/migrations/20200114-01-remove-formid-sha256-constraint.js similarity index 100% rename from lib/model/migrations/legacy/20200114-01-remove-formid-sha256-constraint.js rename to lib/model/migrations/20200114-01-remove-formid-sha256-constraint.js diff --git a/lib/model/migrations/legacy/20200117-01-draft-test-submissions.js b/lib/model/migrations/20200117-01-draft-test-submissions.js similarity index 100% rename from lib/model/migrations/legacy/20200117-01-draft-test-submissions.js rename to lib/model/migrations/20200117-01-draft-test-submissions.js diff --git a/lib/model/migrations/legacy/20200121-01-add-draft-keys.js b/lib/model/migrations/20200121-01-add-draft-keys.js similarity index 100% rename from lib/model/migrations/legacy/20200121-01-add-draft-keys.js rename to lib/model/migrations/20200121-01-add-draft-keys.js diff --git a/lib/model/migrations/legacy/20200122-01-remove-draft-form-state.js b/lib/model/migrations/20200122-01-remove-draft-form-state.js similarity index 100% rename from lib/model/migrations/legacy/20200122-01-remove-draft-form-state.js rename to lib/model/migrations/20200122-01-remove-draft-form-state.js diff --git a/lib/model/migrations/legacy/20200129-01-cascade-submission-deletes.js b/lib/model/migrations/20200129-01-cascade-submission-deletes.js similarity index 100% rename from lib/model/migrations/legacy/20200129-01-cascade-submission-deletes.js rename to lib/model/migrations/20200129-01-cascade-submission-deletes.js diff --git a/lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js b/lib/model/migrations/20200220-01-repair-submission-parsing.js similarity index 100% rename from lib/model/migrations/legacy/20200220-01-repair-submission-parsing.js rename to lib/model/migrations/20200220-01-repair-submission-parsing.js diff --git a/lib/model/migrations/legacy/20200403-01-add-performance-indices.js b/lib/model/migrations/20200403-01-add-performance-indices.js similarity index 100% rename from lib/model/migrations/legacy/20200403-01-add-performance-indices.js rename to lib/model/migrations/20200403-01-add-performance-indices.js diff --git a/lib/model/migrations/legacy/20200407-01-allow-actorless-submission-defs.js b/lib/model/migrations/20200407-01-allow-actorless-submission-defs.js similarity index 100% rename from lib/model/migrations/legacy/20200407-01-allow-actorless-submission-defs.js rename to lib/model/migrations/20200407-01-allow-actorless-submission-defs.js diff --git a/lib/model/migrations/legacy/20200423-01-fix-field-insert-performance.js b/lib/model/migrations/20200423-01-fix-field-insert-performance.js similarity index 100% rename from lib/model/migrations/legacy/20200423-01-fix-field-insert-performance.js rename to lib/model/migrations/20200423-01-fix-field-insert-performance.js diff --git a/lib/model/migrations/legacy/20200428-01-allow-string-downcast.js b/lib/model/migrations/20200428-01-allow-string-downcast.js similarity index 100% rename from lib/model/migrations/legacy/20200428-01-allow-string-downcast.js rename to lib/model/migrations/20200428-01-allow-string-downcast.js diff --git a/lib/model/migrations/legacy/20200519-01-add-enketo-id.js b/lib/model/migrations/20200519-01-add-enketo-id.js similarity index 100% rename from lib/model/migrations/legacy/20200519-01-add-enketo-id.js rename to lib/model/migrations/20200519-01-add-enketo-id.js diff --git a/lib/model/migrations/legacy/20200519-02-add-form-viewer-role.js b/lib/model/migrations/20200519-02-add-form-viewer-role.js similarity index 100% rename from lib/model/migrations/legacy/20200519-02-add-form-viewer-role.js rename to lib/model/migrations/20200519-02-add-form-viewer-role.js diff --git a/lib/model/migrations/legacy/20200520-01-backfill-enketo.js b/lib/model/migrations/20200520-01-backfill-enketo.js similarity index 100% rename from lib/model/migrations/legacy/20200520-01-backfill-enketo.js rename to lib/model/migrations/20200520-01-backfill-enketo.js diff --git a/lib/model/migrations/legacy/20200715-01-add-data-collector-role.js b/lib/model/migrations/20200715-01-add-data-collector-role.js similarity index 100% rename from lib/model/migrations/legacy/20200715-01-add-data-collector-role.js rename to lib/model/migrations/20200715-01-add-data-collector-role.js diff --git a/lib/model/migrations/legacy/20200721-01-add-public-links.js b/lib/model/migrations/20200721-01-add-public-links.js similarity index 100% rename from lib/model/migrations/legacy/20200721-01-add-public-links.js rename to lib/model/migrations/20200721-01-add-public-links.js diff --git a/lib/model/migrations/legacy/20200728-01-add-enketo-single-token-to-forms.js b/lib/model/migrations/20200728-01-add-enketo-single-token-to-forms.js similarity index 100% rename from lib/model/migrations/legacy/20200728-01-add-enketo-single-token-to-forms.js rename to lib/model/migrations/20200728-01-add-enketo-single-token-to-forms.js diff --git a/lib/model/migrations/legacy/20200731-01-allow-project-managers-to-end-sessions.js b/lib/model/migrations/20200731-01-allow-project-managers-to-end-sessions.js similarity index 100% rename from lib/model/migrations/legacy/20200731-01-allow-project-managers-to-end-sessions.js rename to lib/model/migrations/20200731-01-allow-project-managers-to-end-sessions.js diff --git a/lib/model/migrations/legacy/20200810-01-reschedule-enketo-processing.js b/lib/model/migrations/20200810-01-reschedule-enketo-processing.js similarity index 100% rename from lib/model/migrations/legacy/20200810-01-reschedule-enketo-processing.js rename to lib/model/migrations/20200810-01-reschedule-enketo-processing.js diff --git a/lib/model/migrations/legacy/20200918-01-repair-publishedat-dates.js b/lib/model/migrations/20200918-01-repair-publishedat-dates.js similarity index 100% rename from lib/model/migrations/legacy/20200918-01-repair-publishedat-dates.js rename to lib/model/migrations/20200918-01-repair-publishedat-dates.js diff --git a/lib/model/migrations/legacy/20200930-01-add-backup-run-verb.js b/lib/model/migrations/20200930-01-add-backup-run-verb.js similarity index 100% rename from lib/model/migrations/legacy/20200930-01-add-backup-run-verb.js rename to lib/model/migrations/20200930-01-add-backup-run-verb.js diff --git a/lib/model/migrations/legacy/20201117-01-remove-deleted-actor-assignments-again.js b/lib/model/migrations/20201117-01-remove-deleted-actor-assignments-again.js similarity index 100% rename from lib/model/migrations/legacy/20201117-01-remove-deleted-actor-assignments-again.js rename to lib/model/migrations/20201117-01-remove-deleted-actor-assignments-again.js diff --git a/lib/model/migrations/legacy/20201207-01-harmonize-submitter-id-columns.js b/lib/model/migrations/20201207-01-harmonize-submitter-id-columns.js similarity index 100% rename from lib/model/migrations/legacy/20201207-01-harmonize-submitter-id-columns.js rename to lib/model/migrations/20201207-01-harmonize-submitter-id-columns.js diff --git a/lib/model/migrations/legacy/20210118-01-add-current-flag-to-submission-defs.js b/lib/model/migrations/20210118-01-add-current-flag-to-submission-defs.js similarity index 100% rename from lib/model/migrations/legacy/20210118-01-add-current-flag-to-submission-defs.js rename to lib/model/migrations/20210118-01-add-current-flag-to-submission-defs.js diff --git a/lib/model/migrations/legacy/20210120-01-instance-names.js b/lib/model/migrations/20210120-01-instance-names.js similarity index 100% rename from lib/model/migrations/legacy/20210120-01-instance-names.js rename to lib/model/migrations/20210120-01-instance-names.js diff --git a/lib/model/migrations/legacy/20210203-01-add-hierarchy-to-actees.js b/lib/model/migrations/20210203-01-add-hierarchy-to-actees.js similarity index 100% rename from lib/model/migrations/legacy/20210203-01-add-hierarchy-to-actees.js rename to lib/model/migrations/20210203-01-add-hierarchy-to-actees.js diff --git a/lib/model/migrations/legacy/20210210-01-add-instanceid-to-submission-defs.js b/lib/model/migrations/20210210-01-add-instanceid-to-submission-defs.js similarity index 100% rename from lib/model/migrations/legacy/20210210-01-add-instanceid-to-submission-defs.js rename to lib/model/migrations/20210210-01-add-instanceid-to-submission-defs.js diff --git a/lib/model/migrations/legacy/20210218-01-add-submission-edit-verbs.js b/lib/model/migrations/20210218-01-add-submission-edit-verbs.js similarity index 100% rename from lib/model/migrations/legacy/20210218-01-add-submission-edit-verbs.js rename to lib/model/migrations/20210218-01-add-submission-edit-verbs.js diff --git a/lib/model/migrations/legacy/20210218-02-add-draft-to-submissions-unique.js b/lib/model/migrations/20210218-02-add-draft-to-submissions-unique.js similarity index 100% rename from lib/model/migrations/legacy/20210218-02-add-draft-to-submissions-unique.js rename to lib/model/migrations/20210218-02-add-draft-to-submissions-unique.js diff --git a/lib/model/migrations/legacy/20210219-01-add-review-state.js b/lib/model/migrations/20210219-01-add-review-state.js similarity index 100% rename from lib/model/migrations/legacy/20210219-01-add-review-state.js rename to lib/model/migrations/20210219-01-add-review-state.js diff --git a/lib/model/migrations/legacy/20210219-02-add-notes-and-index-to-audits.js b/lib/model/migrations/20210219-02-add-notes-and-index-to-audits.js similarity index 100% rename from lib/model/migrations/legacy/20210219-02-add-notes-and-index-to-audits.js rename to lib/model/migrations/20210219-02-add-notes-and-index-to-audits.js diff --git a/lib/model/migrations/legacy/20210324-01-add-submission-edit-verbs-to-managers.js b/lib/model/migrations/20210324-01-add-submission-edit-verbs-to-managers.js similarity index 100% rename from lib/model/migrations/legacy/20210324-01-add-submission-edit-verbs-to-managers.js rename to lib/model/migrations/20210324-01-add-submission-edit-verbs-to-managers.js diff --git a/lib/model/migrations/legacy/20210325-01-remove-project.list-verb.js b/lib/model/migrations/20210325-01-remove-project.list-verb.js similarity index 100% rename from lib/model/migrations/legacy/20210325-01-remove-project.list-verb.js rename to lib/model/migrations/20210325-01-remove-project.list-verb.js diff --git a/lib/model/migrations/legacy/20210408-01-drop-public-link-createdat.js b/lib/model/migrations/20210408-01-drop-public-link-createdat.js similarity index 100% rename from lib/model/migrations/legacy/20210408-01-drop-public-link-createdat.js rename to lib/model/migrations/20210408-01-drop-public-link-createdat.js diff --git a/lib/model/migrations/legacy/20210408-02-backfill-specialized-actor-audits.js b/lib/model/migrations/20210408-02-backfill-specialized-actor-audits.js similarity index 100% rename from lib/model/migrations/legacy/20210408-02-backfill-specialized-actor-audits.js rename to lib/model/migrations/20210408-02-backfill-specialized-actor-audits.js diff --git a/lib/model/migrations/legacy/20210409-01-add-comments.js b/lib/model/migrations/20210409-01-add-comments.js similarity index 100% rename from lib/model/migrations/legacy/20210409-01-add-comments.js rename to lib/model/migrations/20210409-01-add-comments.js diff --git a/lib/model/migrations/legacy/20210409-02-update-review-states.js b/lib/model/migrations/20210409-02-update-review-states.js similarity index 100% rename from lib/model/migrations/legacy/20210409-02-update-review-states.js rename to lib/model/migrations/20210409-02-update-review-states.js diff --git a/lib/model/migrations/legacy/20210423-01-add-name-to-form-def.js b/lib/model/migrations/20210423-01-add-name-to-form-def.js similarity index 100% rename from lib/model/migrations/legacy/20210423-01-add-name-to-form-def.js rename to lib/model/migrations/20210423-01-add-name-to-form-def.js diff --git a/lib/model/migrations/legacy/20210423-02-drop-form-name.js b/lib/model/migrations/20210423-02-drop-form-name.js similarity index 100% rename from lib/model/migrations/legacy/20210423-02-drop-form-name.js rename to lib/model/migrations/20210423-02-drop-form-name.js diff --git a/lib/model/migrations/legacy/20210716-01-config-value-jsonb.js b/lib/model/migrations/20210716-01-config-value-jsonb.js similarity index 100% rename from lib/model/migrations/legacy/20210716-01-config-value-jsonb.js rename to lib/model/migrations/20210716-01-config-value-jsonb.js diff --git a/lib/model/migrations/legacy/20210721-01-add-config-set-verb.js b/lib/model/migrations/20210721-01-add-config-set-verb.js similarity index 100% rename from lib/model/migrations/legacy/20210721-01-add-config-set-verb.js rename to lib/model/migrations/20210721-01-add-config-set-verb.js diff --git a/lib/model/migrations/legacy/20210817-01-disallow-structure-downcast-to-string.js b/lib/model/migrations/20210817-01-disallow-structure-downcast-to-string.js similarity index 100% rename from lib/model/migrations/legacy/20210817-01-disallow-structure-downcast-to-string.js rename to lib/model/migrations/20210817-01-disallow-structure-downcast-to-string.js diff --git a/lib/model/migrations/legacy/20210825-01-add-analytics-read-verb.js b/lib/model/migrations/20210825-01-add-analytics-read-verb.js similarity index 100% rename from lib/model/migrations/legacy/20210825-01-add-analytics-read-verb.js rename to lib/model/migrations/20210825-01-add-analytics-read-verb.js diff --git a/lib/model/migrations/legacy/20210903-01-backfill-encrypted-client-audits.js b/lib/model/migrations/20210903-01-backfill-encrypted-client-audits.js similarity index 100% rename from lib/model/migrations/legacy/20210903-01-backfill-encrypted-client-audits.js rename to lib/model/migrations/20210903-01-backfill-encrypted-client-audits.js diff --git a/lib/model/migrations/legacy/20210927-01-revert-disallow-structure-downcast.js b/lib/model/migrations/20210927-01-revert-disallow-structure-downcast.js similarity index 100% rename from lib/model/migrations/legacy/20210927-01-revert-disallow-structure-downcast.js rename to lib/model/migrations/20210927-01-revert-disallow-structure-downcast.js diff --git a/lib/model/migrations/legacy/20211008-01-track-select-many-options.js b/lib/model/migrations/20211008-01-track-select-many-options.js similarity index 100% rename from lib/model/migrations/legacy/20211008-01-track-select-many-options.js rename to lib/model/migrations/20211008-01-track-select-many-options.js diff --git a/lib/model/migrations/legacy/20211021-remove-hashes-from-audits.js b/lib/model/migrations/20211021-remove-hashes-from-audits.js similarity index 100% rename from lib/model/migrations/legacy/20211021-remove-hashes-from-audits.js rename to lib/model/migrations/20211021-remove-hashes-from-audits.js diff --git a/lib/model/migrations/legacy/20211109-01-add-user-agent-to-submissions.js b/lib/model/migrations/20211109-01-add-user-agent-to-submissions.js similarity index 100% rename from lib/model/migrations/legacy/20211109-01-add-user-agent-to-submissions.js rename to lib/model/migrations/20211109-01-add-user-agent-to-submissions.js diff --git a/lib/model/migrations/legacy/20211114-01-flag-initial-submission-def.js b/lib/model/migrations/20211114-01-flag-initial-submission-def.js similarity index 100% rename from lib/model/migrations/legacy/20211114-01-flag-initial-submission-def.js rename to lib/model/migrations/20211114-01-flag-initial-submission-def.js diff --git a/lib/model/migrations/legacy/20211117-01-add-form-restore-verb.js b/lib/model/migrations/20211117-01-add-form-restore-verb.js similarity index 100% rename from lib/model/migrations/legacy/20211117-01-add-form-restore-verb.js rename to lib/model/migrations/20211117-01-add-form-restore-verb.js diff --git a/lib/model/migrations/legacy/20211129-01-add-purged-details-to-actees.js b/lib/model/migrations/20211129-01-add-purged-details-to-actees.js similarity index 100% rename from lib/model/migrations/legacy/20211129-01-add-purged-details-to-actees.js rename to lib/model/migrations/20211129-01-add-purged-details-to-actees.js diff --git a/lib/model/migrations/legacy/20220121-01-form-cascade-delete.js b/lib/model/migrations/20220121-01-form-cascade-delete.js similarity index 100% rename from lib/model/migrations/legacy/20220121-01-form-cascade-delete.js rename to lib/model/migrations/20220121-01-form-cascade-delete.js diff --git a/lib/model/migrations/legacy/20220121-02-purge-deleted-forms.js b/lib/model/migrations/20220121-02-purge-deleted-forms.js similarity index 100% rename from lib/model/migrations/legacy/20220121-02-purge-deleted-forms.js rename to lib/model/migrations/20220121-02-purge-deleted-forms.js diff --git a/lib/model/migrations/legacy/20220209-01-purge-unneeded-drafts.js b/lib/model/migrations/20220209-01-purge-unneeded-drafts.js similarity index 100% rename from lib/model/migrations/legacy/20220209-01-purge-unneeded-drafts.js rename to lib/model/migrations/20220209-01-purge-unneeded-drafts.js diff --git a/lib/model/migrations/legacy/20220309-01-add-project-description.js b/lib/model/migrations/20220309-01-add-project-description.js similarity index 100% rename from lib/model/migrations/legacy/20220309-01-add-project-description.js rename to lib/model/migrations/20220309-01-add-project-description.js diff --git a/lib/model/migrations/legacy/20220803-01-create-entities-schema.js b/lib/model/migrations/20220803-01-create-entities-schema.js similarity index 100% rename from lib/model/migrations/legacy/20220803-01-create-entities-schema.js rename to lib/model/migrations/20220803-01-create-entities-schema.js diff --git a/lib/model/migrations/legacy/20221003-01-add-dataset-verbs.js b/lib/model/migrations/20221003-01-add-dataset-verbs.js similarity index 100% rename from lib/model/migrations/legacy/20221003-01-add-dataset-verbs.js rename to lib/model/migrations/20221003-01-add-dataset-verbs.js diff --git a/lib/model/migrations/legacy/20221114-01-explict-dataset-publish.js b/lib/model/migrations/20221114-01-explict-dataset-publish.js similarity index 100% rename from lib/model/migrations/legacy/20221114-01-explict-dataset-publish.js rename to lib/model/migrations/20221114-01-explict-dataset-publish.js diff --git a/lib/model/migrations/legacy/20221117-01-check-datasetId-is-null-for-non-file-type.js b/lib/model/migrations/20221117-01-check-datasetId-is-null-for-non-file-type.js similarity index 100% rename from lib/model/migrations/legacy/20221117-01-check-datasetId-is-null-for-non-file-type.js rename to lib/model/migrations/20221117-01-check-datasetId-is-null-for-non-file-type.js diff --git a/lib/model/migrations/legacy/20221118-01-make-entities-columns-not-null.js b/lib/model/migrations/20221118-01-make-entities-columns-not-null.js similarity index 100% rename from lib/model/migrations/legacy/20221118-01-make-entities-columns-not-null.js rename to lib/model/migrations/20221118-01-make-entities-columns-not-null.js diff --git a/lib/model/migrations/legacy/20221208-01-reduce-tz-precision.js b/lib/model/migrations/20221208-01-reduce-tz-precision.js similarity index 100% rename from lib/model/migrations/legacy/20221208-01-reduce-tz-precision.js rename to lib/model/migrations/20221208-01-reduce-tz-precision.js diff --git a/lib/model/migrations/legacy/20230106-01-remove-revision-number.js b/lib/model/migrations/20230106-01-remove-revision-number.js similarity index 100% rename from lib/model/migrations/legacy/20230106-01-remove-revision-number.js rename to lib/model/migrations/20230106-01-remove-revision-number.js diff --git a/lib/model/migrations/legacy/20230109-01-add-form-schema.js b/lib/model/migrations/20230109-01-add-form-schema.js similarity index 100% rename from lib/model/migrations/legacy/20230109-01-add-form-schema.js rename to lib/model/migrations/20230109-01-add-form-schema.js diff --git a/lib/model/migrations/legacy/20230123-01-remove-google-backups.js b/lib/model/migrations/20230123-01-remove-google-backups.js similarity index 100% rename from lib/model/migrations/legacy/20230123-01-remove-google-backups.js rename to lib/model/migrations/20230123-01-remove-google-backups.js diff --git a/lib/model/migrations/legacy/20230126-01-add-entity-indices.js b/lib/model/migrations/20230126-01-add-entity-indices.js similarity index 100% rename from lib/model/migrations/legacy/20230126-01-add-entity-indices.js rename to lib/model/migrations/20230126-01-add-entity-indices.js diff --git a/lib/model/migrations/legacy/20230127-01-rename-entity-created-by.js b/lib/model/migrations/20230127-01-rename-entity-created-by.js similarity index 100% rename from lib/model/migrations/legacy/20230127-01-rename-entity-created-by.js rename to lib/model/migrations/20230127-01-rename-entity-created-by.js diff --git a/lib/model/migrations/legacy/20230324-01-edit-dataset-verbs.js b/lib/model/migrations/20230324-01-edit-dataset-verbs.js similarity index 100% rename from lib/model/migrations/legacy/20230324-01-edit-dataset-verbs.js rename to lib/model/migrations/20230324-01-edit-dataset-verbs.js diff --git a/lib/model/migrations/legacy/20230406-01-add-entity-def-fields.js b/lib/model/migrations/20230406-01-add-entity-def-fields.js similarity index 100% rename from lib/model/migrations/legacy/20230406-01-add-entity-def-fields.js rename to lib/model/migrations/20230406-01-add-entity-def-fields.js diff --git a/lib/model/migrations/legacy/20230406-02-move-entity-label-add-deletedAt.js b/lib/model/migrations/20230406-02-move-entity-label-add-deletedAt.js similarity index 100% rename from lib/model/migrations/legacy/20230406-02-move-entity-label-add-deletedAt.js rename to lib/model/migrations/20230406-02-move-entity-label-add-deletedAt.js diff --git a/lib/model/migrations/legacy/20230414-01-remove-user-mfa-secret.js b/lib/model/migrations/20230414-01-remove-user-mfa-secret.js similarity index 100% rename from lib/model/migrations/legacy/20230414-01-remove-user-mfa-secret.js rename to lib/model/migrations/20230414-01-remove-user-mfa-secret.js diff --git a/lib/model/migrations/legacy/20230419-01-optimize-indices-sub-defs.js b/lib/model/migrations/20230419-01-optimize-indices-sub-defs.js similarity index 100% rename from lib/model/migrations/legacy/20230419-01-optimize-indices-sub-defs.js rename to lib/model/migrations/20230419-01-optimize-indices-sub-defs.js diff --git a/lib/model/migrations/legacy/20230509-01-dataset-approval-flag.js b/lib/model/migrations/20230509-01-dataset-approval-flag.js similarity index 100% rename from lib/model/migrations/legacy/20230509-01-dataset-approval-flag.js rename to lib/model/migrations/20230509-01-dataset-approval-flag.js diff --git a/lib/model/migrations/legacy/20230512-01-add-entity-root.js b/lib/model/migrations/20230512-01-add-entity-root.js similarity index 100% rename from lib/model/migrations/legacy/20230512-01-add-entity-root.js rename to lib/model/migrations/20230512-01-add-entity-root.js diff --git a/lib/model/migrations/legacy/20230512-02-backfill-entity-id.js b/lib/model/migrations/20230512-02-backfill-entity-id.js similarity index 100% rename from lib/model/migrations/legacy/20230512-02-backfill-entity-id.js rename to lib/model/migrations/20230512-02-backfill-entity-id.js diff --git a/lib/model/migrations/legacy/20230512-03-add-entity-source.js b/lib/model/migrations/20230512-03-add-entity-source.js similarity index 100% rename from lib/model/migrations/legacy/20230512-03-add-entity-source.js rename to lib/model/migrations/20230512-03-add-entity-source.js diff --git a/lib/model/migrations/legacy/20230518-01-add-entity-index-to-audits.js b/lib/model/migrations/20230518-01-add-entity-index-to-audits.js similarity index 100% rename from lib/model/migrations/legacy/20230518-01-add-entity-index-to-audits.js rename to lib/model/migrations/20230518-01-add-entity-index-to-audits.js diff --git a/lib/model/migrations/legacy/20230802-01-delete-orphan-submissions.js b/lib/model/migrations/20230802-01-delete-orphan-submissions.js similarity index 100% rename from lib/model/migrations/legacy/20230802-01-delete-orphan-submissions.js rename to lib/model/migrations/20230802-01-delete-orphan-submissions.js diff --git a/lib/model/migrations/legacy/20230818-01-remove-schemaId-from-dsPropertyFields.js b/lib/model/migrations/20230818-01-remove-schemaId-from-dsPropertyFields.js similarity index 100% rename from lib/model/migrations/legacy/20230818-01-remove-schemaId-from-dsPropertyFields.js rename to lib/model/migrations/20230818-01-remove-schemaId-from-dsPropertyFields.js diff --git a/lib/model/migrations/legacy/20230824-01-add-entity-version.js b/lib/model/migrations/20230824-01-add-entity-version.js similarity index 100% rename from lib/model/migrations/legacy/20230824-01-add-entity-version.js rename to lib/model/migrations/20230824-01-add-entity-version.js diff --git a/lib/model/migrations/legacy/20230830-01-remove-entity-label-from-audits.js b/lib/model/migrations/20230830-01-remove-entity-label-from-audits.js similarity index 100% rename from lib/model/migrations/legacy/20230830-01-remove-entity-label-from-audits.js rename to lib/model/migrations/20230830-01-remove-entity-label-from-audits.js diff --git a/lib/model/migrations/legacy/20230907-01-opened-form-verb.js b/lib/model/migrations/20230907-01-opened-form-verb.js similarity index 100% rename from lib/model/migrations/legacy/20230907-01-opened-form-verb.js rename to lib/model/migrations/20230907-01-opened-form-verb.js diff --git a/lib/model/migrations/legacy/20231002-01-add-conflict-details.js b/lib/model/migrations/20231002-01-add-conflict-details.js similarity index 100% rename from lib/model/migrations/legacy/20231002-01-add-conflict-details.js rename to lib/model/migrations/20231002-01-add-conflict-details.js diff --git a/lib/model/migrations/legacy/20231013-01-change-entity-error-action.js b/lib/model/migrations/20231013-01-change-entity-error-action.js similarity index 100% rename from lib/model/migrations/legacy/20231013-01-change-entity-error-action.js rename to lib/model/migrations/20231013-01-change-entity-error-action.js diff --git a/lib/model/migrations/legacy/20231208-01-dataset-form-def-actions.js b/lib/model/migrations/20231208-01-dataset-form-def-actions.js similarity index 100% rename from lib/model/migrations/legacy/20231208-01-dataset-form-def-actions.js rename to lib/model/migrations/20231208-01-dataset-form-def-actions.js diff --git a/lib/model/migrations/legacy/20240215-01-entity-delete-verb.js b/lib/model/migrations/20240215-01-entity-delete-verb.js similarity index 100% rename from lib/model/migrations/legacy/20240215-01-entity-delete-verb.js rename to lib/model/migrations/20240215-01-entity-delete-verb.js diff --git a/lib/model/migrations/legacy/20240215-02-dedupe-verbs.js b/lib/model/migrations/20240215-02-dedupe-verbs.js similarity index 100% rename from lib/model/migrations/legacy/20240215-02-dedupe-verbs.js rename to lib/model/migrations/20240215-02-dedupe-verbs.js diff --git a/lib/model/migrations/legacy/20240312-01-add-dataset-create-verb.js b/lib/model/migrations/20240312-01-add-dataset-create-verb.js similarity index 100% rename from lib/model/migrations/legacy/20240312-01-add-dataset-create-verb.js rename to lib/model/migrations/20240312-01-add-dataset-create-verb.js diff --git a/lib/model/migrations/legacy/20240322-01-add-entity-source-index-to-audits.js b/lib/model/migrations/20240322-01-add-entity-source-index-to-audits.js similarity index 100% rename from lib/model/migrations/legacy/20240322-01-add-entity-source-index-to-audits.js rename to lib/model/migrations/20240322-01-add-entity-source-index-to-audits.js diff --git a/lib/model/migrations/legacy/20240515-01-entity-tz-precision.js b/lib/model/migrations/20240515-01-entity-tz-precision.js similarity index 100% rename from lib/model/migrations/legacy/20240515-01-entity-tz-precision.js rename to lib/model/migrations/20240515-01-entity-tz-precision.js diff --git a/lib/model/migrations/legacy/20240607-01-add-offline-entity-branch-trunk-info.js b/lib/model/migrations/20240607-01-add-offline-entity-branch-trunk-info.js similarity index 100% rename from lib/model/migrations/legacy/20240607-01-add-offline-entity-branch-trunk-info.js rename to lib/model/migrations/20240607-01-add-offline-entity-branch-trunk-info.js diff --git a/lib/model/migrations/legacy/20240607-02-add-submission-backlog.js b/lib/model/migrations/20240607-02-add-submission-backlog.js similarity index 100% rename from lib/model/migrations/legacy/20240607-02-add-submission-backlog.js rename to lib/model/migrations/20240607-02-add-submission-backlog.js diff --git a/lib/model/migrations/legacy/20240715-01-backlog-add-event-entityuuid.js b/lib/model/migrations/20240715-01-backlog-add-event-entityuuid.js similarity index 100% rename from lib/model/migrations/legacy/20240715-01-backlog-add-event-entityuuid.js rename to lib/model/migrations/20240715-01-backlog-add-event-entityuuid.js diff --git a/lib/model/migrations/legacy/20240913-01-add-blob-s3.js b/lib/model/migrations/20240913-01-add-blob-s3.js similarity index 100% rename from lib/model/migrations/legacy/20240913-01-add-blob-s3.js rename to lib/model/migrations/20240913-01-add-blob-s3.js diff --git a/lib/model/migrations/legacy/20240914-01-add-submission-delete-verb.js b/lib/model/migrations/20240914-01-add-submission-delete-verb.js similarity index 100% rename from lib/model/migrations/legacy/20240914-01-add-submission-delete-verb.js rename to lib/model/migrations/20240914-01-add-submission-delete-verb.js diff --git a/lib/model/migrations/legacy/20240914-02-remove-orphaned-client-audits.js b/lib/model/migrations/20240914-02-remove-orphaned-client-audits.js similarity index 100% rename from lib/model/migrations/legacy/20240914-02-remove-orphaned-client-audits.js rename to lib/model/migrations/20240914-02-remove-orphaned-client-audits.js diff --git a/lib/model/migrations/legacy/20241001-01-index-on-session-table.js b/lib/model/migrations/20241001-01-index-on-session-table.js similarity index 100% rename from lib/model/migrations/legacy/20241001-01-index-on-session-table.js rename to lib/model/migrations/20241001-01-index-on-session-table.js diff --git a/lib/model/migrations/legacy/20241008-01-add-user_preferences.js b/lib/model/migrations/20241008-01-add-user_preferences.js similarity index 100% rename from lib/model/migrations/legacy/20241008-01-add-user_preferences.js rename to lib/model/migrations/20241008-01-add-user_preferences.js diff --git a/lib/model/migrations/legacy/20241010-01-schedule-entity-form-upgrade.js b/lib/model/migrations/20241010-01-schedule-entity-form-upgrade.js similarity index 100% rename from lib/model/migrations/legacy/20241010-01-schedule-entity-form-upgrade.js rename to lib/model/migrations/20241010-01-schedule-entity-form-upgrade.js diff --git a/lib/model/migrations/legacy/20241029-01-schedule-entity-form-upgrade-create-forms.js b/lib/model/migrations/20241029-01-schedule-entity-form-upgrade-create-forms.js similarity index 100% rename from lib/model/migrations/legacy/20241029-01-schedule-entity-form-upgrade-create-forms.js rename to lib/model/migrations/20241029-01-schedule-entity-form-upgrade-create-forms.js diff --git a/lib/model/migrations/legacy/20241030-01-add-force-entity-def-source.js b/lib/model/migrations/20241030-01-add-force-entity-def-source.js similarity index 100% rename from lib/model/migrations/legacy/20241030-01-add-force-entity-def-source.js rename to lib/model/migrations/20241030-01-add-force-entity-def-source.js From de1e5d78c54a4a4c14184716c785963d8018e59c Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Thu, 16 Jan 2025 06:28:02 +0000 Subject: [PATCH 18/44] Add TODOs --- lib/model/migrate.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/model/migrate.js b/lib/model/migrate.js index 6e25cfa3b..27d84ffe8 100644 --- a/lib/model/migrate.js +++ b/lib/model/migrate.js @@ -20,8 +20,8 @@ const { knexConnection } = require('../util/db'); // Connects to the postgres database specified in configuration and returns it. const initKnex = (config) => knex({ client: 'pg', connection: knexConnection(config) }); -const legacyPath = `${__dirname}/migrations`; -const postKnexPath = `${__dirname}/migrations-post-knex`; +const legacyPath = `${__dirname}/migrations`; // TODO rename to /migrations/legacy +const postKnexPath = `${__dirname}/migrations-post-knex`; // TODO rename to /migrations/current // Connects to a database, passes it to a function for operations, then ensures its closure. const withKnex = (config) => (mutator) => { From 887c492c6fbf9e5baf0cea20ff23b3cbacf33f94 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Thu, 16 Jan 2025 06:30:26 +0000 Subject: [PATCH 19/44] remove lint changes to legacy migrations --- lib/model/migrations/.eslintrc.json | 6 ------ lib/model/migrations/20180727-02-add-md5-to-blobs.js | 2 +- .../migrations/20180727-03-add-form-attachments-table.js | 2 +- lib/model/migrations/20190520-01-add-form-versioning.js | 2 +- .../migrations/20191007-01-backfill-client-audits.js | 6 +++--- lib/model/migrations/20191231-02-add-schema-storage.js | 4 ++-- .../migrations/20200220-01-repair-submission-parsing.js | 2 +- lib/model/migrations/20210120-01-instance-names.js | 2 +- .../migrations/20211008-01-track-select-many-options.js | 8 ++++---- lib/model/migrations/20230109-01-add-form-schema.js | 2 +- 10 files changed, 15 insertions(+), 21 deletions(-) delete mode 100644 lib/model/migrations/.eslintrc.json diff --git a/lib/model/migrations/.eslintrc.json b/lib/model/migrations/.eslintrc.json deleted file mode 100644 index 93a99d9dd..000000000 --- a/lib/model/migrations/.eslintrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../../../.eslintrc.json", - "rules": { - "no-restricted-modules": [ "error", { "patterns": [ "../*" ] } ] - } -} diff --git a/lib/model/migrations/20180727-02-add-md5-to-blobs.js b/lib/model/migrations/20180727-02-add-md5-to-blobs.js index 5fa121135..de05f99a4 100644 --- a/lib/model/migrations/20180727-02-add-md5-to-blobs.js +++ b/lib/model/migrations/20180727-02-add-md5-to-blobs.js @@ -8,7 +8,7 @@ // except according to the terms contained in the LICENSE file. // -const { md5sum } = require('../../../util/crypto'); // eslint-disable-line no-restricted-modules +const { md5sum } = require('../../util/crypto'); const up = (knex) => knex.schema.table('blobs', (blobs) => { blobs.string('md5', 32); }) diff --git a/lib/model/migrations/20180727-03-add-form-attachments-table.js b/lib/model/migrations/20180727-03-add-form-attachments-table.js index c48ceb758..80aea61ab 100644 --- a/lib/model/migrations/20180727-03-add-form-attachments-table.js +++ b/lib/model/migrations/20180727-03-add-form-attachments-table.js @@ -23,7 +23,7 @@ const up = (knex) => fa.index([ 'formId' ]); }).then(() => { - const { expectedFormAttachments } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules + const { expectedFormAttachments } = require('../../data/schema'); const { uniq, pluck } = require('ramda'); // now add all expected attachments on extant forms. diff --git a/lib/model/migrations/20190520-01-add-form-versioning.js b/lib/model/migrations/20190520-01-add-form-versioning.js index 623e966c9..2d11c8ce3 100644 --- a/lib/model/migrations/20190520-01-add-form-versioning.js +++ b/lib/model/migrations/20190520-01-add-form-versioning.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { shasum, sha256sum } = require('../../../util/crypto'); // eslint-disable-line no-restricted-modules +const { shasum, sha256sum } = require('../../util/crypto'); const assert = require('assert').strict; const check = (message, query) => diff --git a/lib/model/migrations/20191007-01-backfill-client-audits.js b/lib/model/migrations/20191007-01-backfill-client-audits.js index 5f79a3821..c6551c255 100644 --- a/lib/model/migrations/20191007-01-backfill-client-audits.js +++ b/lib/model/migrations/20191007-01-backfill-client-audits.js @@ -7,9 +7,9 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { parseClientAudits } = require('../../../data/client-audits'); // eslint-disable-line no-restricted-modules -const { getFormFields } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules -const { traverseXml, findOne, root, node, text } = require('../../../util/xml'); // eslint-disable-line no-restricted-modules +const { parseClientAudits } = require('../../data/client-audits'); +const { getFormFields } = require('../../data/schema'); +const { traverseXml, findOne, root, node, text } = require('../../util/xml'); const up = (db) => new Promise((resolve, reject) => { const work = []; diff --git a/lib/model/migrations/20191231-02-add-schema-storage.js b/lib/model/migrations/20191231-02-add-schema-storage.js index da84931f6..ec317539e 100644 --- a/lib/model/migrations/20191231-02-add-schema-storage.js +++ b/lib/model/migrations/20191231-02-add-schema-storage.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { getFormFields } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules +const { getFormFields } = require('../../data/schema'); const up = async (db) => { await db.schema.createTable('form_fields', (fields) => { @@ -51,7 +51,7 @@ const up = async (db) => { // this config hardcoding would be dangerous with tests except that // tests will never invoke this path. const config = require('config').get('default.database'); - const db2 = require('../../migrate').connect(config); // eslint-disable-line no-restricted-modules + const db2 = require('../migrate').connect(config); return db2.select('projectId', 'xmlFormId').from('forms').where({ currentDefId: formDefId }) .then(([{ projectId, xmlFormId }]) => { process.stderr.write(`\n!!!!\nThe database upgrade to v0.8 has failed because the Form '${xmlFormId}' in Project ${projectId} has an invalid schema. It tries to bind multiple instance nodes at the path ${path}.\n!!!!\n\n`); diff --git a/lib/model/migrations/20200220-01-repair-submission-parsing.js b/lib/model/migrations/20200220-01-repair-submission-parsing.js index d4020bc25..d1bd9e0ee 100644 --- a/lib/model/migrations/20200220-01-repair-submission-parsing.js +++ b/lib/model/migrations/20200220-01-repair-submission-parsing.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { Submission } = require('../../frames'); // eslint-disable-line no-restricted-modules +const { Submission } = require('../frames'); const up = async (db) => { const work = []; diff --git a/lib/model/migrations/20210120-01-instance-names.js b/lib/model/migrations/20210120-01-instance-names.js index 6c6cde9d9..832407dfc 100644 --- a/lib/model/migrations/20210120-01-instance-names.js +++ b/lib/model/migrations/20210120-01-instance-names.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { Submission } = require('../../frames'); // eslint-disable-line no-restricted-modules +const { Submission } = require('../frames'); const up = async (db) => { await db.schema.table('submission_defs', (sds) => { diff --git a/lib/model/migrations/20211008-01-track-select-many-options.js b/lib/model/migrations/20211008-01-track-select-many-options.js index 09f1efa92..413e0f1aa 100644 --- a/lib/model/migrations/20211008-01-track-select-many-options.js +++ b/lib/model/migrations/20211008-01-track-select-many-options.js @@ -8,10 +8,10 @@ // except according to the terms contained in the LICENSE file. const { map } = require('ramda'); -const { getFormFields } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules -const { getSelectMultipleResponses } = require('../../../data/submission'); // eslint-disable-line no-restricted-modules -const { Form } = require('../../frames'); // eslint-disable-line no-restricted-modules -const { construct } = require('../../../util/util'); // eslint-disable-line no-restricted-modules +const { getFormFields } = require('../../data/schema'); +const { getSelectMultipleResponses } = require('../../data/submission'); +const { Form } = require('../frames'); +const { construct } = require('../../util/util'); const up = async (db) => { // add select many flag, options field to fields diff --git a/lib/model/migrations/20230109-01-add-form-schema.js b/lib/model/migrations/20230109-01-add-form-schema.js index 63c2cd903..3f591473e 100644 --- a/lib/model/migrations/20230109-01-add-form-schema.js +++ b/lib/model/migrations/20230109-01-add-form-schema.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { getFormFields, compare } = require('../../../data/schema'); // eslint-disable-line no-restricted-modules +const { getFormFields, compare } = require('../../data/schema'); /* Steps of this migration 1. remove check field collision trigger From ed41ada82be991c2c39383ea349e26041fb140e8 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Thu, 16 Jan 2025 06:31:49 +0000 Subject: [PATCH 20/44] revert debug --- lib/task/task.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/task/task.js b/lib/task/task.js index 6f9a1eb13..516e6c29d 100644 --- a/lib/task/task.js +++ b/lib/task/task.js @@ -75,7 +75,6 @@ const writeTo = (output) => (x) => output.write(`${x}\n`); const writeToStderr = writeTo(process.stderr); /* istanbul ignore next */ const fault = (error) => { - console.log('fault()', error); // eslint-disable-line no-console // first print our error. if ((error != null) && (error.isProblem === true) && (error.httpCode < 500)) { writeToStderr(error.message); @@ -106,7 +105,7 @@ const auditing = (action, t) => ((typeof t === 'function') return Promise.resolve(result); }) )), - ((error) => console.log('auditing() error', error) || auditLog(action, false, Option.of(error).map(Problem.serializable).orNull()).then( // eslint-disable-line no-console + ((error) => auditLog(action, false, Option.of(error).map(Problem.serializable).orNull()).then( (() => Promise.reject(error)), ((auditError) => { writeToStderr('Failed to audit-log task failure message!'); From 0769356d494f2a39b3f61d6caa989350af29908c Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Thu, 16 Jan 2025 06:32:36 +0000 Subject: [PATCH 21/44] revert knexConfig name change --- lib/util/db.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/util/db.js b/lib/util/db.js index a095fa400..e6afa12ad 100644 --- a/lib/util/db.js +++ b/lib/util/db.js @@ -52,16 +52,16 @@ const knexConnection = (config) => { const problem = validateConfig(config); if (problem != null) throw problem; // We ignore maximumPoolSize when using Knex. - const { maximumPoolSize, ...rest } = config; - if (rest.ssl === true) { + const { maximumPoolSize, ...knexConfig } = config; + if (knexConfig.ssl === true) { // Slonik seems to specify `false` for `rejectUnauthorized` whenever SSL is // specified: // https://github.com/gajus/slonik/issues/159#issuecomment-891089466. We do // the same here so that Knex will connect to the database in the same way // as Slonik. - rest.ssl = { rejectUnauthorized: false }; + knexConfig.ssl = { rejectUnauthorized: false }; } - return rest; + return knexConfig; }; From c95cec673707e17aa887ed39fb538a3150180392 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Sat, 18 Jan 2025 06:40:33 +0000 Subject: [PATCH 22/44] renames to be less knexy --- lib/bin/check-migrations.js | 7 +-- lib/bin/check-open-db-queries.js | 2 +- lib/bin/run-migrations.js | 10 ++-- lib/model/legacy-knex-migrator.js | 38 ++++++++++++++ .../20191231-02-add-schema-storage.js | 2 +- lib/model/{migrate.js => pg-migrator.js} | 49 +++++-------------- test/integration/other/knex-migrations.js | 2 +- test/integration/setup.js | 2 +- 8 files changed, 60 insertions(+), 52 deletions(-) create mode 100644 lib/model/legacy-knex-migrator.js rename lib/model/{migrate.js => pg-migrator.js} (77%) diff --git a/lib/bin/check-migrations.js b/lib/bin/check-migrations.js index 2119c203d..cc83feeca 100644 --- a/lib/bin/check-migrations.js +++ b/lib/bin/check-migrations.js @@ -7,15 +7,16 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { withKnex, checkKnexMigrations, checkPostKnexMigrations } = require('../model/migrate'); +const { withKnex, checkMigrations } = require('../model/legacy-knex-migrator'); +const { checkPgMigrations } = require('../model/pg-migrator'); // REVIEW why is check-migrations required in the first place? (async () => { try { const config = require('config').get('default.database'); - await withKnex(config)(checkKnexMigrations); - await checkPostKnexMigrations(config); + await withKnex(config)(checkMigrations); + await checkPgMigrations(config); } catch (err) { console.error('Error:', err.message); process.exit(1); diff --git a/lib/bin/check-open-db-queries.js b/lib/bin/check-open-db-queries.js index 3299f839a..89b952cef 100644 --- a/lib/bin/check-open-db-queries.js +++ b/lib/bin/check-open-db-queries.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { withKnex } = require('../model/migrate'); +const { withKnex } = require('../model/legacy-knex-migrator'); (async () => { try { diff --git a/lib/bin/run-migrations.js b/lib/bin/run-migrations.js index 10e9d3edd..6c0f59232 100644 --- a/lib/bin/run-migrations.js +++ b/lib/bin/run-migrations.js @@ -7,18 +7,14 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { // eslint-disable-line object-curly-newline - withKnex, - knexMigrations, - - postKnexMigrations, -} = require('../model/migrate'); // eslint-disable-line object-curly-newline +const { withKnex, knexMigrations } = require('../model/legacy-knex-migrator'); +const { pgMigrations } = require('../model/pg-migrator'); (async () => { try { const config = require('config').get('default.database'); await withKnex(config)(knexMigrations); - await postKnexMigrations(config); + await pgMigrations(config); } catch (err) { console.error(err); process.exit(1); diff --git a/lib/model/legacy-knex-migrator.js b/lib/model/legacy-knex-migrator.js new file mode 100644 index 000000000..c021b0653 --- /dev/null +++ b/lib/model/legacy-knex-migrator.js @@ -0,0 +1,38 @@ +// Copyright 2017 ODK Central Developers +// See the NOTICE file at the top-level directory of this distribution and at +// https://github.com/getodk/central-backend/blob/master/NOTICE. +// This file is part of ODK Central. It is subject to the license terms in +// the LICENSE file found in the top-level directory of this distribution and at +// https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central, +// including this file, may be copied, modified, propagated, or distributed +// except according to the terms contained in the LICENSE file. +// +// This is a variety of functions helpful for connecting to and performing +// top-level operations with a database, like migrations. + +const knex = require('knex'); +const { knexConnection } = require('../util/db'); + +// Connects to the postgres database specified in configuration and returns it. +const knexConnect = (config) => knex({ client: 'pg', connection: knexConnection(config) }); + +const legacyPath = `${__dirname}/migrations`; // TODO rename to /migrations/legacy + +// Connects to a database, passes it to a function for operations, then ensures its closure. +const withKnex = (config) => (mutator) => { + const db = knexConnect(config); + return mutator(db).finally(() => db.destroy()); +}; + +// Given a database, initiates migrations on it. +const knexMigrations = (db) => db.migrate.latest({ directory: legacyPath }); + +// Checks for pending migrations and returns an exit code of 1 if any are +// still pending/unapplied (e.g. automatically running migrations just failed). +const checkMigrations = (db) => db.migrate.list({ directory: legacyPath }) + .then((res) => { + if (res[1].length > 0) + process.exitCode = 1; + }); + +module.exports = { checkMigrations, knexConnect, withKnex, knexMigrations }; diff --git a/lib/model/migrations/20191231-02-add-schema-storage.js b/lib/model/migrations/20191231-02-add-schema-storage.js index d71604530..d04bd6049 100644 --- a/lib/model/migrations/20191231-02-add-schema-storage.js +++ b/lib/model/migrations/20191231-02-add-schema-storage.js @@ -51,7 +51,7 @@ const up = async (db) => { // this config hardcoding would be dangerous with tests except that // tests will never invoke this path. const config = require('config').get('default.database'); - const db2 = require('../migrate').knexConnect(config); + const db2 = require('../legacy-knex-migrator').knexConnect(config); return db2.select('projectId', 'xmlFormId').from('forms').where({ currentDefId: formDefId }) .then(([{ projectId, xmlFormId }]) => { process.stderr.write(`\n!!!!\nThe database upgrade to v0.8 has failed because the Form '${xmlFormId}' in Project ${projectId} has an invalid schema. It tries to bind multiple instance nodes at the path ${path}.\n!!!!\n\n`); diff --git a/lib/model/migrate.js b/lib/model/pg-migrator.js similarity index 77% rename from lib/model/migrate.js rename to lib/model/pg-migrator.js index 33bba706d..eb936b7be 100644 --- a/lib/model/migrate.js +++ b/lib/model/pg-migrator.js @@ -1,4 +1,4 @@ -// Copyright 2017 ODK Central Developers +// Copyright 2025 ODK Central Developers // See the NOTICE file at the top-level directory of this distribution and at // https://github.com/getodk/central-backend/blob/master/NOTICE. // This file is part of ODK Central. It is subject to the license terms in @@ -6,31 +6,13 @@ // https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central, // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -// -// This is a variety of functions helpful for connecting to and performing -// top-level operations with a database, like migrations. const { lstatSync, readdirSync } = require('node:fs'); const _ = require('lodash'); // eslint-disable-line import/no-extraneous-dependencies -const knex = require('knex'); const pg = require('pg'); -const { knexConnection } = require('../util/db'); -// Connects to the postgres database specified in configuration and returns it. -const knexConnect = (config) => knex({ client: 'pg', connection: knexConnection(config) }); - -const legacyPath = `${__dirname}/migrations`; // TODO rename to /migrations/legacy -const postKnexPath = `${__dirname}/migrations-post-knex`; // TODO rename to /migrations/current - -// Connects to a database, passes it to a function for operations, then ensures its closure. -const withKnex = (config) => (mutator) => { - const db = knexConnect(config); - return mutator(db).finally(() => db.destroy()); -}; - -// Given a database, initiates migrations on it. -const knexMigrations = (db) => db.migrate.latest({ directory: legacyPath }); +const migrationsDir = `${__dirname}/migrations-post-knex`; // TODO rename to /migrations/current or something const withPg = async (config, fn) => { const log = (...args) => console.log('[withPg]', ...args); // eslint-disable-line no-console @@ -54,11 +36,10 @@ const withPg = async (config, fn) => { } }; -const getPostKnexMigrationsToRun = async client => { - const log = (...args) => console.log('[getPostKnexMigrationsToRun]', ...args); // eslint-disable-line no-console +const getMigrationsToRun = async client => { + const log = (...args) => console.log('[getMigrationsToRun]', ...args); // eslint-disable-line no-console log('ENTRY'); - const migrationsDir = postKnexPath; const allMigrations = readdirSync(migrationsDir) .filter(f => f.endsWith('.js') && lstatSync(`${migrationsDir}/${f}`).isFile()) .sort(); @@ -80,8 +61,8 @@ const getPostKnexMigrationsToRun = async client => { return toRun; }; -const postKnexMigrations = async (config) => { - const log = (...args) => console.log('[postKnexMigrations]', ...args); // eslint-disable-line no-console +const pgMigrations = async (config) => { + const log = (...args) => console.log('[pgMigrations]', ...args); // eslint-disable-line no-console log('ENTRY'); // In the main, this migrator is written to behave similarly to knex's: @@ -126,7 +107,7 @@ const postKnexMigrations = async (config) => { await client.query('LOCK TABLE post_knex_migrations IN EXCLUSIVE MODE NOWAIT'); log('Lock acquired.'); - const toRun = await getPostKnexMigrationsToRun(client); + const toRun = await getMigrationsToRun(client); if (!toRun.length) { log('No migrations to run - exiting.'); @@ -195,22 +176,14 @@ const postKnexMigrations = async (config) => { // Checks for pending migrations and returns an exit code of 1 if any are // still pending/unapplied (e.g. automatically running migrations just failed). -const checkKnexMigrations = (db) => db.migrate.list({ directory: legacyPath }) - .then((res) => { - if (res[1].length > 0) - process.exitCode = 1; - }); - -// Checks for pending migrations and returns an exit code of 1 if any are -// still pending/unapplied (e.g. automatically running migrations just failed). -const checkPostKnexMigrations = async config => { - const log = (...args) => console.log('[checkPostKnexMigrations]', ...args); // eslint-disable-line no-console +const checkPgMigrations = async config => { + const log = (...args) => console.log('[checkPgMigrations]', ...args); // eslint-disable-line no-console log('ENTRY'); await withPg(config, async client => { - const toRun = await getPostKnexMigrationsToRun(client); + const toRun = await getMigrationsToRun(client); if (toRun.length) process.exitCode = 1; }); }; -module.exports = { checkKnexMigrations, checkPostKnexMigrations, knexConnect, withKnex, withPg, knexMigrations, postKnexMigrations }; +module.exports = { checkPgMigrations, withPg, pgMigrations }; diff --git a/test/integration/other/knex-migrations.js b/test/integration/other/knex-migrations.js index 29b1c5a84..870a58b5b 100644 --- a/test/integration/other/knex-migrations.js +++ b/test/integration/other/knex-migrations.js @@ -6,7 +6,7 @@ const { testContainerFullTrx, testServiceFullTrx } = require('../setup'); const { sql } = require('slonik'); const { createReadStream } = require('fs'); const { Actor, Config } = require(appRoot + '/lib/model/frames'); -const { withKnex } = require(appRoot + '/lib/model/migrate'); +const { withKnex } = require(appRoot + '/lib/model/legacy-knex-migrator'); const { exhaust } = require(appRoot + '/lib/worker/worker'); const testData = require('../../data/xml'); diff --git a/test/integration/setup.js b/test/integration/setup.js index 5db664093..586eccfc8 100644 --- a/test/integration/setup.js +++ b/test/integration/setup.js @@ -12,7 +12,7 @@ const testData = require('../data/xml'); // knex things. const config = require('config'); -const { withPg } = require(appRoot + '/lib/model/migrate'); +const { withPg } = require(appRoot + '/lib/model/pg-migrator'); // slonik connection pool const { slonikPool } = require(appRoot + '/lib/external/slonik'); From 322c5ba4aa848958150b9681d989b72ceeabc3d1 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Sat, 18 Jan 2025 08:13:55 +0000 Subject: [PATCH 23/44] reduce changeset --- lib/bin/check-migrations.js | 7 +++---- lib/bin/check-open-db-queries.js | 2 +- lib/bin/run-migrations.js | 9 ++++----- lib/model/{legacy-knex-migrator.js => migrate.js} | 11 ++++++----- .../migrations/20191231-02-add-schema-storage.js | 2 +- .../other/{knex-migrations.js => migrations.js} | 5 +++-- 6 files changed, 18 insertions(+), 18 deletions(-) rename lib/model/{legacy-knex-migrator.js => migrate.js} (80%) rename test/integration/other/{knex-migrations.js => migrations.js} (99%) diff --git a/lib/bin/check-migrations.js b/lib/bin/check-migrations.js index cc83feeca..19919672a 100644 --- a/lib/bin/check-migrations.js +++ b/lib/bin/check-migrations.js @@ -7,16 +7,15 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { withKnex, checkMigrations } = require('../model/legacy-knex-migrator'); +const { withKnex, checkMigrations } = require('../model/migrate'); const { checkPgMigrations } = require('../model/pg-migrator'); // REVIEW why is check-migrations required in the first place? (async () => { try { - const config = require('config').get('default.database'); - await withKnex(config)(checkMigrations); - await checkPgMigrations(config); + await withKnex(require('config').get('default.database'))(checkMigrations); + await checkPgMigrations(require('config').get('default.database')); } catch (err) { console.error('Error:', err.message); process.exit(1); diff --git a/lib/bin/check-open-db-queries.js b/lib/bin/check-open-db-queries.js index 89b952cef..3299f839a 100644 --- a/lib/bin/check-open-db-queries.js +++ b/lib/bin/check-open-db-queries.js @@ -7,7 +7,7 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { withKnex } = require('../model/legacy-knex-migrator'); +const { withKnex } = require('../model/migrate'); (async () => { try { diff --git a/lib/bin/run-migrations.js b/lib/bin/run-migrations.js index 6c0f59232..09141f4f4 100644 --- a/lib/bin/run-migrations.js +++ b/lib/bin/run-migrations.js @@ -7,16 +7,15 @@ // including this file, may be copied, modified, propagated, or distributed // except according to the terms contained in the LICENSE file. -const { withKnex, knexMigrations } = require('../model/legacy-knex-migrator'); +const { withKnex, migrate } = require('../model/migrate'); const { pgMigrations } = require('../model/pg-migrator'); (async () => { try { - const config = require('config').get('default.database'); - await withKnex(config)(knexMigrations); - await pgMigrations(config); + await withKnex(require('config').get('default.database'))(migrate); + await pgMigrations(require('config').get('default.database')); } catch (err) { - console.error(err); + console.error('Error:', err.message); process.exit(1); } })(); diff --git a/lib/model/legacy-knex-migrator.js b/lib/model/migrate.js similarity index 80% rename from lib/model/legacy-knex-migrator.js rename to lib/model/migrate.js index c021b0653..0e494df8e 100644 --- a/lib/model/legacy-knex-migrator.js +++ b/lib/model/migrate.js @@ -10,14 +10,15 @@ // This is a variety of functions helpful for connecting to and performing // top-level operations with a database, like migrations. +// TODO rename e.g. legacy-knex-migrator +// TODO move migration files to e.g. /migrations/legacy + const knex = require('knex'); const { knexConnection } = require('../util/db'); // Connects to the postgres database specified in configuration and returns it. const knexConnect = (config) => knex({ client: 'pg', connection: knexConnection(config) }); -const legacyPath = `${__dirname}/migrations`; // TODO rename to /migrations/legacy - // Connects to a database, passes it to a function for operations, then ensures its closure. const withKnex = (config) => (mutator) => { const db = knexConnect(config); @@ -25,14 +26,14 @@ const withKnex = (config) => (mutator) => { }; // Given a database, initiates migrations on it. -const knexMigrations = (db) => db.migrate.latest({ directory: legacyPath }); +const migrate = (db) => db.migrate.latest({ directory: `${__dirname}/migrations` }); // Checks for pending migrations and returns an exit code of 1 if any are // still pending/unapplied (e.g. automatically running migrations just failed). -const checkMigrations = (db) => db.migrate.list({ directory: legacyPath }) +const checkMigrations = (db) => db.migrate.list({ directory: `${__dirname}/migrations` }) .then((res) => { if (res[1].length > 0) process.exitCode = 1; }); -module.exports = { checkMigrations, knexConnect, withKnex, knexMigrations }; +module.exports = { checkMigrations, knexConnect, withKnex, migrate }; diff --git a/lib/model/migrations/20191231-02-add-schema-storage.js b/lib/model/migrations/20191231-02-add-schema-storage.js index d04bd6049..d71604530 100644 --- a/lib/model/migrations/20191231-02-add-schema-storage.js +++ b/lib/model/migrations/20191231-02-add-schema-storage.js @@ -51,7 +51,7 @@ const up = async (db) => { // this config hardcoding would be dangerous with tests except that // tests will never invoke this path. const config = require('config').get('default.database'); - const db2 = require('../legacy-knex-migrator').knexConnect(config); + const db2 = require('../migrate').knexConnect(config); return db2.select('projectId', 'xmlFormId').from('forms').where({ currentDefId: formDefId }) .then(([{ projectId, xmlFormId }]) => { process.stderr.write(`\n!!!!\nThe database upgrade to v0.8 has failed because the Form '${xmlFormId}' in Project ${projectId} has an invalid schema. It tries to bind multiple instance nodes at the path ${path}.\n!!!!\n\n`); diff --git a/test/integration/other/knex-migrations.js b/test/integration/other/migrations.js similarity index 99% rename from test/integration/other/knex-migrations.js rename to test/integration/other/migrations.js index 870a58b5b..1775696f3 100644 --- a/test/integration/other/knex-migrations.js +++ b/test/integration/other/migrations.js @@ -1,3 +1,4 @@ +// TODO rename e.g. legacy-knex-migrations const { readFileSync } = require('fs'); const appRoot = require('app-root-path'); const uuid = require('uuid').v4; @@ -6,7 +7,7 @@ const { testContainerFullTrx, testServiceFullTrx } = require('../setup'); const { sql } = require('slonik'); const { createReadStream } = require('fs'); const { Actor, Config } = require(appRoot + '/lib/model/frames'); -const { withKnex } = require(appRoot + '/lib/model/legacy-knex-migrator'); +const { withKnex } = require(appRoot + '/lib/model/migrate'); const { exhaust } = require(appRoot + '/lib/worker/worker'); const testData = require('../../data/xml'); @@ -59,7 +60,7 @@ testMigration.skip = (filename, tests) => // column to projects and forms, it is not possible to migrate part way // (before the new column) and populate the data when frames expect the // new column to exist. -describe.skip('legacy (knex) database migrations', function() { +describe.skip('database migrations', function() { this.timeout(8000); it('should purge deleted forms via migration', testServiceFullTrx(async (service, container) => { From 6c1a62b18ad21c716c6d6bc9a65670e57dbe871a Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Sat, 18 Jan 2025 08:14:52 +0000 Subject: [PATCH 24/44] revert whitespace change --- lib/model/migrate.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/model/migrate.js b/lib/model/migrate.js index 0e494df8e..2d5b04ee6 100644 --- a/lib/model/migrate.js +++ b/lib/model/migrate.js @@ -37,3 +37,4 @@ const checkMigrations = (db) => db.migrate.list({ directory: `${__dirname}/migra }); module.exports = { checkMigrations, knexConnect, withKnex, migrate }; + From a415a70df6c6547720edb025e685616fe486489f Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 21 Jan 2025 14:05:30 +0000 Subject: [PATCH 25/44] import fixes for null content types --- lib/model/query/blobs.js | 2 +- lib/model/query/submission-attachments.js | 6 +++--- lib/model/query/submissions.js | 22 +++++----------------- 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/lib/model/query/blobs.js b/lib/model/query/blobs.js index 6808cce58..81e476089 100644 --- a/lib/model/query/blobs.js +++ b/lib/model/query/blobs.js @@ -22,7 +22,7 @@ const { construct } = require('../../util/util'); const ensure = (blob) => ({ oneFirst }) => oneFirst(sql` with ensured as (insert into blobs (sha, md5, content, "contentType") values - (${blob.sha}, ${blob.md5}, ${sql.binary(blob.content)}, ${blob.contentType || null}) + (${blob.sha}, ${blob.md5}, ${sql.binary(blob.content)}, ${blob.contentType || sql`DEFAULT`}) on conflict (sha) do update set sha = ${blob.sha} returning id) select id from ensured`); diff --git a/lib/model/query/submission-attachments.js b/lib/model/query/submission-attachments.js index aaf5657fc..8a2901b8f 100644 --- a/lib/model/query/submission-attachments.js +++ b/lib/model/query/submission-attachments.js @@ -107,12 +107,12 @@ const upsert = (def, files) => ({ Blobs, SubmissionAttachments }) => const present = files.filter((file) => lookup.has(file.fieldname)); return Promise.all(present .map((file) => Blobs.ensure(Blob.fromBuffer(file.buffer, file.mimetype)) - .then((blobId) => SubmissionAttachments.attach(def.id, file.fieldname, blobId)))); + .then((blobId) => SubmissionAttachments.attach(def, file.fieldname, blobId)))); }); -const attach = (defId, name, blobId) => ({ run }) => run(sql` +const attach = (def, name, blobId) => ({ run }) => run(sql` update submission_attachments set "blobId"=${blobId} -where "submissionDefId"=${defId} and name=${name}`); +where "submissionDefId"=${def.id} and name=${name}`); // TODO: this is currently audit logged in resource/submissions. probably deal w it here instead. diff --git a/lib/model/query/submissions.js b/lib/model/query/submissions.js index 40d6630af..ef7a07e31 100644 --- a/lib/model/query/submissions.js +++ b/lib/model/query/submissions.js @@ -18,7 +18,6 @@ const { unjoiner, extender, sqlEquals, page, updater, QueryOptions, insertMany, const { blankStringToNull, construct } = require('../../util/util'); const Problem = require('../../util/problem'); const { streamEncBlobs } = require('../../util/blob'); -const Option = require('../../util/option'); //////////////////////////////////////////////////////////////////////////////// @@ -270,8 +269,8 @@ where submissions."instanceId"=${instanceId} and submission_defs.current=true`) .then(map((row) => new Submission(row, { def: new Submission.Def({ id: row.defId }) }))); -const _buildGetCurrentSql = (cols, projectId, xmlFormId, instanceId, draft) => sql` -select ${cols} from submission_defs +const getCurrentDefByIds = (projectId, xmlFormId, instanceId, draft) => ({ maybeOne }) => maybeOne(sql` +select submission_defs.* from submission_defs inner join (select submissions.id, "instanceId" from submissions inner join @@ -281,19 +280,8 @@ inner join where submissions."deletedAt" is null and draft=${draft}) as submissions on submissions.id=submission_defs."submissionId" where submissions."instanceId"=${instanceId} and current=true -limit 1`; - -const getCurrentDefColByIds = (col, projectId, xmlFormId, instanceId, draft) => ({ maybeOneFirst }) => - maybeOneFirst(_buildGetCurrentSql(sql.identifier(['submission_defs', col]), projectId, xmlFormId, instanceId, draft)) - .then(map(Option.of)); - -const getCurrentDefColsByIds = (cols, projectId, xmlFormId, instanceId, draft) => ({ maybeOne }) => - maybeOne(_buildGetCurrentSql(sql.join(cols.map(col => sql.identifier(['submission_defs', col])), sql`,`), projectId, xmlFormId, instanceId, draft)) - .then(map(Option.of)); - -const getCurrentDefByIds = (projectId, xmlFormId, instanceId, draft) => ({ maybeOne }) => - maybeOne(_buildGetCurrentSql(sql`submission_defs.*`, projectId, xmlFormId, instanceId, draft)) - .then(map(construct(Submission.Def))); +limit 1`) + .then(map(construct(Submission.Def))); const getDefById = (submissionDefId) => ({ maybeOne }) => maybeOne(sql` select submission_defs.* from submission_defs @@ -492,7 +480,7 @@ module.exports = { setSelectMultipleValues, getSelectMultipleValuesForExport, getByIdsWithDef, getSubAndDefById, getByIds, getAllForFormByIds, getById, countByFormId, verifyVersion, - getDefById, getCurrentDefByIds, getCurrentDefColByIds, getCurrentDefColsByIds, getAnyDefByFormAndInstanceId, getDefsByFormAndLogicalId, getDefBySubmissionAndInstanceId, getRootForInstanceId, + getDefById, getCurrentDefByIds, getAnyDefByFormAndInstanceId, getDefsByFormAndLogicalId, getDefBySubmissionAndInstanceId, getRootForInstanceId, getDeleted, streamForExport, getForExport }; From 5d6218bbcad659aa6700709ee4b6d0f3423d67de Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 21 Jan 2025 14:06:21 +0000 Subject: [PATCH 26/44] import query changes --- lib/model/query/submission-attachments.js | 6 +++--- lib/model/query/submissions.js | 22 +++++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/model/query/submission-attachments.js b/lib/model/query/submission-attachments.js index 8a2901b8f..aaf5657fc 100644 --- a/lib/model/query/submission-attachments.js +++ b/lib/model/query/submission-attachments.js @@ -107,12 +107,12 @@ const upsert = (def, files) => ({ Blobs, SubmissionAttachments }) => const present = files.filter((file) => lookup.has(file.fieldname)); return Promise.all(present .map((file) => Blobs.ensure(Blob.fromBuffer(file.buffer, file.mimetype)) - .then((blobId) => SubmissionAttachments.attach(def, file.fieldname, blobId)))); + .then((blobId) => SubmissionAttachments.attach(def.id, file.fieldname, blobId)))); }); -const attach = (def, name, blobId) => ({ run }) => run(sql` +const attach = (defId, name, blobId) => ({ run }) => run(sql` update submission_attachments set "blobId"=${blobId} -where "submissionDefId"=${def.id} and name=${name}`); +where "submissionDefId"=${defId} and name=${name}`); // TODO: this is currently audit logged in resource/submissions. probably deal w it here instead. diff --git a/lib/model/query/submissions.js b/lib/model/query/submissions.js index ef7a07e31..40d6630af 100644 --- a/lib/model/query/submissions.js +++ b/lib/model/query/submissions.js @@ -18,6 +18,7 @@ const { unjoiner, extender, sqlEquals, page, updater, QueryOptions, insertMany, const { blankStringToNull, construct } = require('../../util/util'); const Problem = require('../../util/problem'); const { streamEncBlobs } = require('../../util/blob'); +const Option = require('../../util/option'); //////////////////////////////////////////////////////////////////////////////// @@ -269,8 +270,8 @@ where submissions."instanceId"=${instanceId} and submission_defs.current=true`) .then(map((row) => new Submission(row, { def: new Submission.Def({ id: row.defId }) }))); -const getCurrentDefByIds = (projectId, xmlFormId, instanceId, draft) => ({ maybeOne }) => maybeOne(sql` -select submission_defs.* from submission_defs +const _buildGetCurrentSql = (cols, projectId, xmlFormId, instanceId, draft) => sql` +select ${cols} from submission_defs inner join (select submissions.id, "instanceId" from submissions inner join @@ -280,8 +281,19 @@ inner join where submissions."deletedAt" is null and draft=${draft}) as submissions on submissions.id=submission_defs."submissionId" where submissions."instanceId"=${instanceId} and current=true -limit 1`) - .then(map(construct(Submission.Def))); +limit 1`; + +const getCurrentDefColByIds = (col, projectId, xmlFormId, instanceId, draft) => ({ maybeOneFirst }) => + maybeOneFirst(_buildGetCurrentSql(sql.identifier(['submission_defs', col]), projectId, xmlFormId, instanceId, draft)) + .then(map(Option.of)); + +const getCurrentDefColsByIds = (cols, projectId, xmlFormId, instanceId, draft) => ({ maybeOne }) => + maybeOne(_buildGetCurrentSql(sql.join(cols.map(col => sql.identifier(['submission_defs', col])), sql`,`), projectId, xmlFormId, instanceId, draft)) + .then(map(Option.of)); + +const getCurrentDefByIds = (projectId, xmlFormId, instanceId, draft) => ({ maybeOne }) => + maybeOne(_buildGetCurrentSql(sql`submission_defs.*`, projectId, xmlFormId, instanceId, draft)) + .then(map(construct(Submission.Def))); const getDefById = (submissionDefId) => ({ maybeOne }) => maybeOne(sql` select submission_defs.* from submission_defs @@ -480,7 +492,7 @@ module.exports = { setSelectMultipleValues, getSelectMultipleValuesForExport, getByIdsWithDef, getSubAndDefById, getByIds, getAllForFormByIds, getById, countByFormId, verifyVersion, - getDefById, getCurrentDefByIds, getAnyDefByFormAndInstanceId, getDefsByFormAndLogicalId, getDefBySubmissionAndInstanceId, getRootForInstanceId, + getDefById, getCurrentDefByIds, getCurrentDefColByIds, getCurrentDefColsByIds, getAnyDefByFormAndInstanceId, getDefsByFormAndLogicalId, getDefBySubmissionAndInstanceId, getRootForInstanceId, getDeleted, streamForExport, getForExport }; From fafc6fb8af27229d220f49ddd841080d9a1fb023 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 21 Jan 2025 14:06:54 +0000 Subject: [PATCH 27/44] e2e test fix? --- test/e2e/s3/test.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/e2e/s3/test.js b/test/e2e/s3/test.js index b94c2e75c..c599a44bb 100644 --- a/test/e2e/s3/test.js +++ b/test/e2e/s3/test.js @@ -379,9 +379,7 @@ describe('s3 support', () => { const filepath = `${attDir}/${name}`; - // "null" is a questionable content-type, but matches current central behaviour - // See: https://github.com/getodk/central-backend/pull/1352 - const expectedContentType = mimetypeFor(name) ?? 'null'; + const expectedContentType = mimetypeFor(name) ?? 'application/octet-stream'; const actualContentType = res.headers.get('content-type'); should.equal(actualContentType, expectedContentType); From 8c6cf2910ab79be8339d4a7a9fd4208b1dc186c0 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 21 Jan 2025 14:07:58 +0000 Subject: [PATCH 28/44] import test changes --- test/integration/api/submissions.js | 18 ++++++++---------- test/integration/task/s3.js | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/test/integration/api/submissions.js b/test/integration/api/submissions.js index 6a9580c0b..0c21d0076 100644 --- a/test/integration/api/submissions.js +++ b/test/integration/api/submissions.js @@ -4377,8 +4377,7 @@ one,h,/data/h,2000-01-01T00:06,2000-01-01T00:07,-5,-6,,ee,ff body.toString().should.equal('testvideo'); }))))))); - // Ref https://github.com/getodk/central-backend/issues/1351 - it('should attach a given file with empty Content-Type', testService((service) => + it('should attach a given file with empty Content-Type and serve it with default mime type', testService((service) => service.login('alice', (asAlice) => asAlice.post('/v1/projects/1/forms?publish=true') .set('Content-Type', 'application/xml') @@ -4394,13 +4393,12 @@ one,h,/data/h,2000-01-01T00:06,2000-01-01T00:07,-5,-6,,ee,ff .expect(200) .then(() => asAlice.get('/v1/projects/1/forms/binaryType/submissions/both/attachments/my_file1.mp4') .expect(200) - .then(({ headers, text }) => { - headers['content-type'].should.equal('null'); - text.toString().should.equal('testvideo'); // use 'text' instead of 'body' to avoid supertest response parsing + .then(({ headers, body }) => { + headers['content-type'].should.equal('application/octet-stream'); + body.toString().should.equal('testvideo'); }))))))); - // Ref https://github.com/getodk/central-backend/issues/1351 - it('should attach a given file with missing Content-Type', testService((service) => + it('should attach a given file with missing Content-Type and serve it with default mime type', testService((service) => service.login('alice', (asAlice) => asAlice.post('/v1/projects/1/forms?publish=true') .set('Content-Type', 'application/xml') @@ -4416,9 +4414,9 @@ one,h,/data/h,2000-01-01T00:06,2000-01-01T00:07,-5,-6,,ee,ff .expect(200) .then(() => asAlice.get('/v1/projects/1/forms/binaryType/submissions/both/attachments/my_file1.mp4') .expect(200) - .then(({ headers, text }) => { - headers['content-type'].should.equal('null'); - text.toString().should.equal('testvideo'); // use 'text' instead of 'body' to avoid supertest response parsing + .then(({ headers, body }) => { + headers['content-type'].should.equal('application/octet-stream'); + body.toString().should.equal('testvideo'); }))))))); it('should log an audit entry about initial attachment', testService((service, { Audits, Forms, Submissions, SubmissionAttachments }) => diff --git a/test/integration/task/s3.js b/test/integration/task/s3.js index 0c3b07285..f9d58d874 100644 --- a/test/integration/task/s3.js +++ b/test/integration/task/s3.js @@ -11,7 +11,7 @@ const aBlobExistsWith = async (container, { status }) => { const blob = await Blob.fromBuffer(crypto.randomBytes(100)); await container.run(sql` INSERT INTO BLOBS (sha, md5, content, "contentType", s3_status) - VALUES (${blob.sha}, ${blob.md5}, ${sql.binary(blob.content)}, ${blob.contentType || null}, ${status}) + VALUES (${blob.sha}, ${blob.md5}, ${sql.binary(blob.content)}, ${blob.contentType || sql`DEFAULT`}, ${status}) `); }; From b264a83b37e302fff1fa63de4e011030154ed031 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 21 Jan 2025 14:12:19 +0000 Subject: [PATCH 29/44] ci/db-migrations: add new path --- .github/workflows/db-migrations.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/db-migrations.yml b/.github/workflows/db-migrations.yml index 8b6594c9c..4a33bf04a 100644 --- a/.github/workflows/db-migrations.yml +++ b/.github/workflows/db-migrations.yml @@ -6,6 +6,7 @@ on: - .github/workflows/db-migrations.yml - lib/bin/create-docker-databases.js - lib/model/migrations/** + - lib/model/migrations-post-knex/** - test/db-migrations/** - package.json - package-lock.json @@ -15,6 +16,7 @@ on: - .github/workflows/db-migrations.yml - lib/bin/create-docker-databases.js - lib/model/migrations/** + - lib/model/migrations-post-knex/** - test/db-migrations/** - package.json - package-lock.json From 9a1db566367abb07e8b349a4b08ce37db0ec7df2 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 21 Jan 2025 14:28:37 +0000 Subject: [PATCH 30/44] Make migration tests pass? --- .../20241008-01-add-user_preferences.spec.js | 4 +- ...isable-nullable-blob-content-types.spec.js | 4 +- test/db-migrations/migrator.js | 147 +++++++++--------- test/db-migrations/utils.js | 50 ++++-- 4 files changed, 122 insertions(+), 83 deletions(-) diff --git a/test/db-migrations/20241008-01-add-user_preferences.spec.js b/test/db-migrations/20241008-01-add-user_preferences.spec.js index 8a89e1eea..74a70dd20 100644 --- a/test/db-migrations/20241008-01-add-user_preferences.spec.js +++ b/test/db-migrations/20241008-01-add-user_preferences.spec.js @@ -2,10 +2,10 @@ const { // eslint-disable-line object-curly-newline assertIndexExists, assertTableDoesNotExist, assertTableSchema, - describeMigration, + describeLegacyMigration, } = require('./utils'); // eslint-disable-line object-curly-newline -describeMigration('20241008-01-add-user_preferences', ({ runMigrationBeingTested }) => { +describeLegacyMigration('20241008-01-add-user_preferences', ({ runMigrationBeingTested }) => { before(async () => { await assertTableDoesNotExist('user_site_preferences'); await assertTableDoesNotExist('user_project_preferences'); diff --git a/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js b/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js index e3fb41c03..c8b307e56 100644 --- a/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js +++ b/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js @@ -3,11 +3,11 @@ const { hash, randomBytes } = require('node:crypto'); const { // eslint-disable-line object-curly-newline assertTableContents, - describeMigration, + describeNewMigration, rowsExistFor, } = require('./utils'); // eslint-disable-line object-curly-newline -describeMigration('20250113-01-disable-nullable-blob-content-types', ({ runMigrationBeingTested }) => { +describeNewMigration('20250113-01-disable-nullable-blob-content-types', ({ runMigrationBeingTested }) => { const aBlobWith = props => { const randomContent = randomBytes(100); const md5 = hash('md5', randomContent); // eslint-disable-line no-multi-spaces diff --git a/test/db-migrations/migrator.js b/test/db-migrations/migrator.js index 739b8d971..73353679b 100644 --- a/test/db-migrations/migrator.js +++ b/test/db-migrations/migrator.js @@ -15,94 +15,101 @@ const fs = require('node:fs'); const { execSync } = require('node:child_process'); -const migrationsDir = './lib/model/migrations'; -const holdingPen = './test/db-migrations/.holding-pen'; +const legacy = createMigrator('./lib/model/migrations', './test/db-migrations/.holding-pen/legacy'); +const postKnex = createMigrator('./lib/model/migrations-post-knex', './test/db-migrations/.holding-pen/post-knex', legacy); -fs.mkdirSync(holdingPen, { recursive: true }); +module.exports = { legacy, postKnex }; -restoreMigrations(); // eslint-disable-line no-use-before-define -const allMigrations = loadMigrationsList(); // eslint-disable-line no-use-before-define -moveMigrationsToHoldingPen(); // eslint-disable-line no-use-before-define +function createMigrator(migrationsDir, holdingPen, previousMigrator) { + fs.mkdirSync(holdingPen, { recursive: true }); -let lastRunIdx = -1; + restoreMigrations(); // eslint-disable-line no-use-before-define + const allMigrations = loadMigrationsList(); // eslint-disable-line no-use-before-define + moveMigrationsToHoldingPen(); // eslint-disable-line no-use-before-define -function runBefore(migrationName) { - const idx = getIndex(migrationName); // eslint-disable-line no-use-before-define - if (idx === 0) return; + let lastRunIdx = -1; - const previousMigration = allMigrations[idx - 1]; + return { + exists, + hasRun, + runBefore, + runIncluding, + restoreMigrations, + }; - return runIncluding(previousMigration); // eslint-disable-line no-use-before-define -} + function runBefore(migrationName) { + const idx = getIndex(migrationName); // eslint-disable-line no-use-before-define + if (idx === 0) return; -function runIncluding(lastMigrationToRun) { - const finalIdx = getIndex(lastMigrationToRun); // eslint-disable-line no-use-before-define + const previousMigration = allMigrations[idx - 1]; - for (let restoreIdx=lastRunIdx+1; restoreIdx<=finalIdx; ++restoreIdx) { // eslint-disable-line no-plusplus - const f = allMigrations[restoreIdx] + '.js'; - fs.renameSync(`${holdingPen}/${f}`, `${migrationsDir}/${f}`); + return runIncluding(previousMigration); // eslint-disable-line no-use-before-define } - log('Running migrations until:', lastMigrationToRun, '...'); - const res = execSync(`node ./lib/bin/run-migrations.js`, { encoding: 'utf8' }); + function runIncluding(lastMigrationToRun) { + if(previousMigrator) previousMigrator.restoreMigrations(); - lastRunIdx = finalIdx; + const finalIdx = getIndex(lastMigrationToRun); // eslint-disable-line no-use-before-define - log(`Ran migrations up-to-and-including ${lastMigrationToRun}:\n`, res); -} + for (let restoreIdx=lastRunIdx+1; restoreIdx<=finalIdx; ++restoreIdx) { // eslint-disable-line no-plusplus + const f = allMigrations[restoreIdx] + '.js'; + fs.renameSync(`${holdingPen}/${f}`, `${migrationsDir}/${f}`); + } -function getIndex(migrationName) { - const idx = allMigrations.indexOf(migrationName); - log('getIndex()', migrationName, 'found at', idx); - if (idx === -1) throw new Error(`Unknown migration: ${migrationName}`); - return idx; -} + log('Running migrations until:', lastMigrationToRun, '...'); + const res = execSync(`node ./lib/bin/run-migrations.js`, { encoding: 'utf8' }); -function restoreMigrations() { - moveAll(holdingPen, migrationsDir); // eslint-disable-line no-use-before-define -} + lastRunIdx = finalIdx; -function moveMigrationsToHoldingPen() { - moveAll(migrationsDir, holdingPen); // eslint-disable-line no-use-before-define -} + log(`Ran migrations up-to-and-including ${lastMigrationToRun}:\n`, res); + } -function moveAll(src, tgt) { - fs.readdirSync(src) - .forEach(f => fs.renameSync(`${src}/${f}`, `${tgt}/${f}`)); -} + function getIndex(migrationName) { + const idx = allMigrations.indexOf(migrationName); + log('getIndex()', migrationName, 'found at', idx); + if (idx === -1) throw new Error(`Unknown migration: ${migrationName}`); + return idx; + } -function loadMigrationsList() { - const migrations = fs.readdirSync(migrationsDir) - .filter(f => f.endsWith('.js')) - .map(f => f.replace(/\.js$/, '')) - .sort(); // TODO check that this is how knex sorts migration files - log(); - log('All migrations:'); - log(); - migrations.forEach(m => log('*', m)); - log(); - log('Total:', migrations.length); - log(); - return migrations; -} + function restoreMigrations() { + moveAll(holdingPen, migrationsDir); // eslint-disable-line no-use-before-define + } -function exists(migrationName) { - try { - getIndex(migrationName); - return true; - } catch (err) { - return false; + function moveMigrationsToHoldingPen() { + moveAll(migrationsDir, holdingPen); // eslint-disable-line no-use-before-define } -} -function hasRun(migrationName) { - return lastRunIdx >= getIndex(migrationName); -} + function moveAll(src, tgt) { + fs.readdirSync(src) + .forEach(f => fs.renameSync(`${src}/${f}`, `${tgt}/${f}`)); + } + + function loadMigrationsList() { + const migrations = fs.readdirSync(migrationsDir) + .filter(f => f.endsWith('.js')) + .map(f => f.replace(/\.js$/, '')) + .sort(); // TODO check that this is how knex sorts migration files + log(); + log('All migrations:'); + log(); + migrations.forEach(m => log('*', m)); + log(); + log('Total:', migrations.length); + log(); + return migrations; + } -module.exports = { - exists, - hasRun, - runBefore, - runIncluding, - restoreMigrations, -}; + function exists(migrationName) { + console.log('migrator.exists()', migrationName, { allMigrations }); + try { + getIndex(migrationName); + return true; + } catch (err) { + return false; + } + } + + function hasRun(migrationName) { + return lastRunIdx >= getIndex(migrationName); + } +} diff --git a/test/db-migrations/utils.js b/test/db-migrations/utils.js index ad382e9fd..8f799ac52 100644 --- a/test/db-migrations/utils.js +++ b/test/db-migrations/utils.js @@ -2,13 +2,13 @@ const assert = require('node:assert/strict'); const _ = require('lodash'); const migrator = require('./migrator'); -function _describeMigration(describeFn, migrationName, fn) { +function _describeLegacyMigration(describeFn, migrationName, fn) { assert.strictEqual(arguments.length, 3, 'Incorrect argument count.'); assert.strictEqual(typeof describeFn, 'function'); - assert.ok(migrator.exists(migrationName), `Migration '${migrationName}' does not exist.`); - assert.ok(!migrator.hasRun(migrationName), `Migration '${migrationName}' has already been run.`); + assert.ok(migrator.legacy.exists(migrationName), `Migration '${migrationName}' does not exist.`); + assert.ok(!migrator.legacy.hasRun(migrationName), `Migration '${migrationName}' has already been run.`); assert.strictEqual(typeof fn, 'function'); assert.strictEqual(fn.length, 1); @@ -18,20 +18,51 @@ function _describeMigration(describeFn, migrationName, fn) { return () => { if (alreadyRun) throw new Error('Migration has already run! Check your test structure.'); alreadyRun = true; - migrator.runIncluding(migrationName); + migrator.legacy.runIncluding(migrationName); }; })(); return describeFn(`database migration: ${migrationName}`, () => { before(() => { - migrator.runBefore(migrationName); + migrator.legacy.runBefore(migrationName); }); return fn({ runMigrationBeingTested }); }); } -function describeMigration(...args) { return _describeMigration(describe, ...args); } -describeMigration.only = (...args) => _describeMigration(describe.only, ...args); // eslint-disable-line no-only-tests/no-only-tests, no-multi-spaces -describeMigration.skip = (...args) => _describeMigration(describe.skip, ...args); // eslint-disable-line no-multi-spaces +function describeLegacyMigration(...args) { return _describeLegacyMigration(describe, ...args); } +describeLegacyMigration.only = (...args) => _describeLegacyMigration(describe.only, ...args); // eslint-disable-line no-only-tests/no-only-tests, no-multi-spaces +describeLegacyMigration.skip = (...args) => _describeLegacyMigration(describe.skip, ...args); // eslint-disable-line no-multi-spaces + +function _describeNewMigration(describeFn, migrationName, fn) { + assert.strictEqual(arguments.length, 3, 'Incorrect argument count.'); + + assert.strictEqual(typeof describeFn, 'function'); + + assert.ok(migrator.postKnex.exists(migrationName), `Migration '${migrationName}' does not exist.`); + assert.ok(!migrator.postKnex.hasRun(migrationName), `Migration '${migrationName}' has already been run.`); + + assert.strictEqual(typeof fn, 'function'); + assert.strictEqual(fn.length, 1); + + const runMigrationBeingTested = (() => { + let alreadyRun; + return () => { + if (alreadyRun) throw new Error('Migration has already run! Check your test structure.'); + alreadyRun = true; + migrator.postKnex.runIncluding(migrationName); + }; + })(); + + return describeFn(`database migration: ${migrationName}`, () => { + before(() => { + migrator.postKnex.runBefore(migrationName); + }); + return fn({ runMigrationBeingTested }); + }); +} +function describeNewMigration(...args) { return _describeNewMigration(describe, ...args); } +describeNewMigration.only = (...args) => _describeNewMigration(describe.only, ...args); // eslint-disable-line no-only-tests/no-only-tests, no-multi-spaces +describeNewMigration.skip = (...args) => _describeNewMigration(describe.skip, ...args); // eslint-disable-line no-multi-spaces async function assertIndexExists(tableName, expected) { if (arguments.length !== 2) throw new Error('Incorrect arg count.'); @@ -180,7 +211,8 @@ module.exports = { assertTableDoesNotExist, assertTableSchema, - describeMigration, + describeLegacyMigration, + describeNewMigration, // TODO rename to simply describeMigration rowsExistFor, }; From 9987704bd54728fa00028a49c506c5024a562133 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 21 Jan 2025 14:29:27 +0000 Subject: [PATCH 31/44] only move js? --- test/db-migrations/migrator.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/db-migrations/migrator.js b/test/db-migrations/migrator.js index 73353679b..346db9fe4 100644 --- a/test/db-migrations/migrator.js +++ b/test/db-migrations/migrator.js @@ -81,6 +81,7 @@ function createMigrator(migrationsDir, holdingPen, previousMigrator) { function moveAll(src, tgt) { fs.readdirSync(src) + .filter(f => f.endsWith('.js')) .forEach(f => fs.renameSync(`${src}/${f}`, `${tgt}/${f}`)); } From f20dad47c1f4704257a8af13f86c0d8c01b3fc5a Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 21 Jan 2025 14:32:01 +0000 Subject: [PATCH 32/44] lint --- test/db-migrations/migrator.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/test/db-migrations/migrator.js b/test/db-migrations/migrator.js index 346db9fe4..bc4ee14c0 100644 --- a/test/db-migrations/migrator.js +++ b/test/db-migrations/migrator.js @@ -15,8 +15,8 @@ const fs = require('node:fs'); const { execSync } = require('node:child_process'); -const legacy = createMigrator('./lib/model/migrations', './test/db-migrations/.holding-pen/legacy'); -const postKnex = createMigrator('./lib/model/migrations-post-knex', './test/db-migrations/.holding-pen/post-knex', legacy); +const legacy = createMigrator('./lib/model/migrations', './test/db-migrations/.holding-pen/legacy'); // eslint-disable-line no-use-before-define, no-multi-spaces +const postKnex = createMigrator('./lib/model/migrations-post-knex', './test/db-migrations/.holding-pen/post-knex', legacy); // eslint-disable-line no-use-before-define module.exports = { legacy, postKnex }; @@ -30,11 +30,11 @@ function createMigrator(migrationsDir, holdingPen, previousMigrator) { let lastRunIdx = -1; return { - exists, - hasRun, - runBefore, - runIncluding, - restoreMigrations, + exists, // eslint-disable-line no-use-before-define, no-multi-spaces + hasRun, // eslint-disable-line no-use-before-define, no-multi-spaces + runBefore, // eslint-disable-line no-use-before-define, no-multi-spaces + runIncluding, // eslint-disable-line no-use-before-define, no-multi-spaces + restoreMigrations, // eslint-disable-line no-use-before-define }; function runBefore(migrationName) { @@ -47,7 +47,7 @@ function createMigrator(migrationsDir, holdingPen, previousMigrator) { } function runIncluding(lastMigrationToRun) { - if(previousMigrator) previousMigrator.restoreMigrations(); + if (previousMigrator) previousMigrator.restoreMigrations(); const finalIdx = getIndex(lastMigrationToRun); // eslint-disable-line no-use-before-define @@ -101,7 +101,6 @@ function createMigrator(migrationsDir, holdingPen, previousMigrator) { } function exists(migrationName) { - console.log('migrator.exists()', migrationName, { allMigrations }); try { getIndex(migrationName); return true; From 8aa22863fcb6a445666f68cd8da11611dfa2eb1f Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 21 Jan 2025 14:43:46 +0000 Subject: [PATCH 33/44] wip --- test/db-migrations/utils.js | 48 +++++++++---------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/test/db-migrations/utils.js b/test/db-migrations/utils.js index 8f799ac52..dd54ba1d2 100644 --- a/test/db-migrations/utils.js +++ b/test/db-migrations/utils.js @@ -2,13 +2,13 @@ const assert = require('node:assert/strict'); const _ = require('lodash'); const migrator = require('./migrator'); -function _describeLegacyMigration(describeFn, migrationName, fn) { +function _describeMigration(migrator, describeFn, migrationName, fn) { // eslint-disable-line no-shadow assert.strictEqual(arguments.length, 3, 'Incorrect argument count.'); assert.strictEqual(typeof describeFn, 'function'); - assert.ok(migrator.legacy.exists(migrationName), `Migration '${migrationName}' does not exist.`); - assert.ok(!migrator.legacy.hasRun(migrationName), `Migration '${migrationName}' has already been run.`); + assert.ok(migrator.exists(migrationName), `Migration '${migrationName}' does not exist.`); + assert.ok(!migrator.hasRun(migrationName), `Migration '${migrationName}' has already been run.`); assert.strictEqual(typeof fn, 'function'); assert.strictEqual(fn.length, 1); @@ -18,51 +18,25 @@ function _describeLegacyMigration(describeFn, migrationName, fn) { return () => { if (alreadyRun) throw new Error('Migration has already run! Check your test structure.'); alreadyRun = true; - migrator.legacy.runIncluding(migrationName); + migrator.runIncluding(migrationName); }; })(); return describeFn(`database migration: ${migrationName}`, () => { before(() => { - migrator.legacy.runBefore(migrationName); + migrator.runBefore(migrationName); }); return fn({ runMigrationBeingTested }); }); } -function describeLegacyMigration(...args) { return _describeLegacyMigration(describe, ...args); } -describeLegacyMigration.only = (...args) => _describeLegacyMigration(describe.only, ...args); // eslint-disable-line no-only-tests/no-only-tests, no-multi-spaces -describeLegacyMigration.skip = (...args) => _describeLegacyMigration(describe.skip, ...args); // eslint-disable-line no-multi-spaces -function _describeNewMigration(describeFn, migrationName, fn) { - assert.strictEqual(arguments.length, 3, 'Incorrect argument count.'); - - assert.strictEqual(typeof describeFn, 'function'); +function describeLegacyMigration(...args) { return _describeMigration(migrator.legacy, describe, ...args); } // eslint-disable-line no-multi-spaces +describeLegacyMigration.only = (...args) => _describeMigration(migrator.legacy, describe.only, ...args); // eslint-disable-line no-only-tests/no-only-tests, no-multi-spaces +describeLegacyMigration.skip = (...args) => _describeMigration(migrator.legacy, describe.skip, ...args); // eslint-disable-line no-multi-spaces - assert.ok(migrator.postKnex.exists(migrationName), `Migration '${migrationName}' does not exist.`); - assert.ok(!migrator.postKnex.hasRun(migrationName), `Migration '${migrationName}' has already been run.`); - - assert.strictEqual(typeof fn, 'function'); - assert.strictEqual(fn.length, 1); - - const runMigrationBeingTested = (() => { - let alreadyRun; - return () => { - if (alreadyRun) throw new Error('Migration has already run! Check your test structure.'); - alreadyRun = true; - migrator.postKnex.runIncluding(migrationName); - }; - })(); - - return describeFn(`database migration: ${migrationName}`, () => { - before(() => { - migrator.postKnex.runBefore(migrationName); - }); - return fn({ runMigrationBeingTested }); - }); -} -function describeNewMigration(...args) { return _describeNewMigration(describe, ...args); } -describeNewMigration.only = (...args) => _describeNewMigration(describe.only, ...args); // eslint-disable-line no-only-tests/no-only-tests, no-multi-spaces -describeNewMigration.skip = (...args) => _describeNewMigration(describe.skip, ...args); // eslint-disable-line no-multi-spaces +function describeNewMigration(...args) { return _describeMigration(migrator.postKnex, describe, ...args); } // eslint-disable-line no-multi-spaces +describeNewMigration.only = (...args) => _describeMigration(migrator.postKnex, describe.only, ...args); // eslint-disable-line no-only-tests/no-only-tests, no-multi-spaces +describeNewMigration.skip = (...args) => _describeMigration(migrator.postKnex, describe.skip, ...args); // eslint-disable-line no-multi-spaces async function assertIndexExists(tableName, expected) { if (arguments.length !== 2) throw new Error('Incorrect arg count.'); From 62f043df4693fab5c8b5761f65946600083ea8a1 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 21 Jan 2025 14:45:44 +0000 Subject: [PATCH 34/44] add migrator name --- test/db-migrations/migrator.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/db-migrations/migrator.js b/test/db-migrations/migrator.js index bc4ee14c0..16527f9e5 100644 --- a/test/db-migrations/migrator.js +++ b/test/db-migrations/migrator.js @@ -15,12 +15,12 @@ const fs = require('node:fs'); const { execSync } = require('node:child_process'); -const legacy = createMigrator('./lib/model/migrations', './test/db-migrations/.holding-pen/legacy'); // eslint-disable-line no-use-before-define, no-multi-spaces -const postKnex = createMigrator('./lib/model/migrations-post-knex', './test/db-migrations/.holding-pen/post-knex', legacy); // eslint-disable-line no-use-before-define +const legacy = createMigrator('Legacy', './lib/model/migrations', './test/db-migrations/.holding-pen/legacy'); // eslint-disable-line no-use-before-define, no-multi-spaces +const postKnex = createMigrator('Post-knex', './lib/model/migrations-post-knex', './test/db-migrations/.holding-pen/post-knex', legacy); // eslint-disable-line no-use-before-define module.exports = { legacy, postKnex }; -function createMigrator(migrationsDir, holdingPen, previousMigrator) { +function createMigrator(name, migrationsDir, holdingPen, previousMigrator) { fs.mkdirSync(holdingPen, { recursive: true }); restoreMigrations(); // eslint-disable-line no-use-before-define @@ -91,7 +91,7 @@ function createMigrator(migrationsDir, holdingPen, previousMigrator) { .map(f => f.replace(/\.js$/, '')) .sort(); // TODO check that this is how knex sorts migration files log(); - log('All migrations:'); + log(`${name} migrations:`); log(); migrations.forEach(m => log('*', m)); log(); From ec35f7c2d3857cfa173b90151abd05cc79943585 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 21 Jan 2025 14:46:26 +0000 Subject: [PATCH 35/44] correct arg count --- test/db-migrations/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/db-migrations/utils.js b/test/db-migrations/utils.js index dd54ba1d2..841f3f9fc 100644 --- a/test/db-migrations/utils.js +++ b/test/db-migrations/utils.js @@ -3,7 +3,7 @@ const _ = require('lodash'); const migrator = require('./migrator'); function _describeMigration(migrator, describeFn, migrationName, fn) { // eslint-disable-line no-shadow - assert.strictEqual(arguments.length, 3, 'Incorrect argument count.'); + assert.strictEqual(arguments.length, 4, 'Incorrect argument count.'); assert.strictEqual(typeof describeFn, 'function'); From 08f235c029067fd02049263cacef91f45d1ccd2b Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 4 Feb 2025 05:53:23 +0000 Subject: [PATCH 36/44] add check --- .../20250113-01-disable-nullable-blob-content-types.spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js b/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js index 922102f38..b96715448 100644 --- a/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js +++ b/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js @@ -22,6 +22,8 @@ describeNewMigration('20250113-01-disable-nullable-blob-content-types', ({ runMi before(async () => { await rowsExistFor('blobs', blob1, blob2); + await assertTableContents('blobs', blob1, blob2); // TODO should fail if old migration still exists + await runMigrationBeingTested(); }); From 3f8aa1277f265e0b9a00fa63061d9d30e3338c8b Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 4 Feb 2025 05:55:15 +0000 Subject: [PATCH 37/44] rename new migration --- ...es.js => 20250204-01-disable-nullable-blob-content-types.js} | 0 ... => 20250204-01-disable-nullable-blob-content-types.spec.js} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/model/migrations-post-knex/{20250113-01-disable-nullable-blob-content-types.js => 20250204-01-disable-nullable-blob-content-types.js} (100%) rename test/db-migrations/{20250113-01-disable-nullable-blob-content-types.spec.js => 20250204-01-disable-nullable-blob-content-types.spec.js} (96%) diff --git a/lib/model/migrations-post-knex/20250113-01-disable-nullable-blob-content-types.js b/lib/model/migrations-post-knex/20250204-01-disable-nullable-blob-content-types.js similarity index 100% rename from lib/model/migrations-post-knex/20250113-01-disable-nullable-blob-content-types.js rename to lib/model/migrations-post-knex/20250204-01-disable-nullable-blob-content-types.js diff --git a/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js b/test/db-migrations/20250204-01-disable-nullable-blob-content-types.spec.js similarity index 96% rename from test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js rename to test/db-migrations/20250204-01-disable-nullable-blob-content-types.spec.js index b96715448..9b33aba49 100644 --- a/test/db-migrations/20250113-01-disable-nullable-blob-content-types.spec.js +++ b/test/db-migrations/20250204-01-disable-nullable-blob-content-types.spec.js @@ -7,7 +7,7 @@ const { // eslint-disable-line object-curly-newline rowsExistFor, } = require('./utils'); // eslint-disable-line object-curly-newline -describeNewMigration('20250113-01-disable-nullable-blob-content-types', ({ runMigrationBeingTested }) => { +describeNewMigration('20250204-01-disable-nullable-blob-content-types', ({ runMigrationBeingTested }) => { const aBlobWith = props => { const randomContent = randomBytes(100); const md5 = hash('md5', randomContent); // eslint-disable-line no-multi-spaces From 26fe2ea0a691f9cb37050f1ad0c5d5c515c1ef79 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 4 Feb 2025 06:06:57 +0000 Subject: [PATCH 38/44] Fix test migrator --- test/db-migrations/migrator.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/db-migrations/migrator.js b/test/db-migrations/migrator.js index 16527f9e5..c8dd510f0 100644 --- a/test/db-migrations/migrator.js +++ b/test/db-migrations/migrator.js @@ -39,23 +39,25 @@ function createMigrator(name, migrationsDir, holdingPen, previousMigrator) { function runBefore(migrationName) { const idx = getIndex(migrationName); // eslint-disable-line no-use-before-define - if (idx === 0) return; - - const previousMigration = allMigrations[idx - 1]; - - return runIncluding(previousMigration); // eslint-disable-line no-use-before-define + runUntilIndex(idx - 1); } function runIncluding(lastMigrationToRun) { - if (previousMigrator) previousMigrator.restoreMigrations(); - - const finalIdx = getIndex(lastMigrationToRun); // eslint-disable-line no-use-before-define + runUntilIndex(getIndex(lastMigrationToRun)); // eslint-disable-line no-use-before-define + } + function runUntilIndex(finalIdx) { for (let restoreIdx=lastRunIdx+1; restoreIdx<=finalIdx; ++restoreIdx) { // eslint-disable-line no-plusplus const f = allMigrations[restoreIdx] + '.js'; fs.renameSync(`${holdingPen}/${f}`, `${migrationsDir}/${f}`); } + if (previousMigrator) { + log('Restoring migrations for previousMigrator...'); + previousMigrator.restoreMigrations(); + } + + const lastMigrationToRun = allMigrations[finalIdx]; log('Running migrations until:', lastMigrationToRun, '...'); const res = execSync(`node ./lib/bin/run-migrations.js`, { encoding: 'utf8' }); From 2d015c81dace45870661cc9213bf91325e952c7b Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 4 Feb 2025 06:10:16 +0000 Subject: [PATCH 39/44] update --- .../20250204-01-disable-nullable-blob-content-types.spec.js | 3 +-- test/db-migrations/migrator.js | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/db-migrations/20250204-01-disable-nullable-blob-content-types.spec.js b/test/db-migrations/20250204-01-disable-nullable-blob-content-types.spec.js index 9b33aba49..9ab3b8357 100644 --- a/test/db-migrations/20250204-01-disable-nullable-blob-content-types.spec.js +++ b/test/db-migrations/20250204-01-disable-nullable-blob-content-types.spec.js @@ -21,8 +21,7 @@ describeNewMigration('20250204-01-disable-nullable-blob-content-types', ({ runMi before(async () => { await rowsExistFor('blobs', blob1, blob2); - - await assertTableContents('blobs', blob1, blob2); // TODO should fail if old migration still exists + await assertTableContents('blobs', blob1, blob2); // should fail if old migration still exists await runMigrationBeingTested(); }); diff --git a/test/db-migrations/migrator.js b/test/db-migrations/migrator.js index c8dd510f0..4594cdd4c 100644 --- a/test/db-migrations/migrator.js +++ b/test/db-migrations/migrator.js @@ -74,6 +74,7 @@ function createMigrator(name, migrationsDir, holdingPen, previousMigrator) { } function restoreMigrations() { + log('restoring migrations from ', holdingPen, 'to', migrationsDir, '...'); moveAll(holdingPen, migrationsDir); // eslint-disable-line no-use-before-define } @@ -84,7 +85,7 @@ function createMigrator(name, migrationsDir, holdingPen, previousMigrator) { function moveAll(src, tgt) { fs.readdirSync(src) .filter(f => f.endsWith('.js')) - .forEach(f => fs.renameSync(`${src}/${f}`, `${tgt}/${f}`)); + .forEach(f => log(`Moving ${f} from ${src} to ${tgt}...`) || fs.renameSync(`${src}/${f}`, `${tgt}/${f}`)); } function loadMigrationsList() { From b21b48669c3038ae5934f17bac0ff0292f207e6e Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 4 Feb 2025 06:11:15 +0000 Subject: [PATCH 40/44] remove logging --- test/db-migrations/migrator.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/db-migrations/migrator.js b/test/db-migrations/migrator.js index 4594cdd4c..613f5a6e9 100644 --- a/test/db-migrations/migrator.js +++ b/test/db-migrations/migrator.js @@ -52,10 +52,7 @@ function createMigrator(name, migrationsDir, holdingPen, previousMigrator) { fs.renameSync(`${holdingPen}/${f}`, `${migrationsDir}/${f}`); } - if (previousMigrator) { - log('Restoring migrations for previousMigrator...'); - previousMigrator.restoreMigrations(); - } + if (previousMigrator) previousMigrator.restoreMigrations(); const lastMigrationToRun = allMigrations[finalIdx]; log('Running migrations until:', lastMigrationToRun, '...'); @@ -74,7 +71,6 @@ function createMigrator(name, migrationsDir, holdingPen, previousMigrator) { } function restoreMigrations() { - log('restoring migrations from ', holdingPen, 'to', migrationsDir, '...'); moveAll(holdingPen, migrationsDir); // eslint-disable-line no-use-before-define } @@ -85,7 +81,7 @@ function createMigrator(name, migrationsDir, holdingPen, previousMigrator) { function moveAll(src, tgt) { fs.readdirSync(src) .filter(f => f.endsWith('.js')) - .forEach(f => log(`Moving ${f} from ${src} to ${tgt}...`) || fs.renameSync(`${src}/${f}`, `${tgt}/${f}`)); + .forEach(f => fs.renameSync(`${src}/${f}`, `${tgt}/${f}`)); } function loadMigrationsList() { From 4c5ce3a4bd957aaa1b7dcfc7926e1b0072795990 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 4 Feb 2025 06:12:03 +0000 Subject: [PATCH 41/44] remove superseded migration --- ...-01-disable-nullable-blob-content-types.js | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js diff --git a/lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js b/lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js deleted file mode 100644 index b3844bc3a..000000000 --- a/lib/model/migrations/20250113-01-disable-nullable-blob-content-types.js +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2025 ODK Central Developers -// See the NOTICE file at the top-level directory of this distribution and at -// https://github.com/getodk/central-backend/blob/master/NOTICE. -// This file is part of ODK Central. It is subject to the license terms in -// the LICENSE file found in the top-level directory of this distribution and at -// https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central, -// including this file, may be copied, modified, propagated, or distributed -// except according to the terms contained in the LICENSE file. - -const up = (db) => db.raw(` - UPDATE blobs SET "contentType"='application/octet-stream' WHERE "contentType" IS NULL; - ALTER TABLE blobs - ALTER COLUMN "contentType" SET DEFAULT 'application/octet-stream', - ALTER COLUMN "contentType" SET NOT NULL -`); - -const down = (db) => db.raw(` - ALTER TABLE blobs - ALTER COLUMN "contentType" DROP NOT NULL, - ALTER COLUMN "contentType" DROP DEFAULT -`); - -module.exports = { up, down }; From a15fe022760cb0286f490a45e6c01c7941fb3ef4 Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 4 Feb 2025 06:13:41 +0000 Subject: [PATCH 42/44] lint --- test/db-migrations/migrator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/db-migrations/migrator.js b/test/db-migrations/migrator.js index 613f5a6e9..682046896 100644 --- a/test/db-migrations/migrator.js +++ b/test/db-migrations/migrator.js @@ -39,7 +39,7 @@ function createMigrator(name, migrationsDir, holdingPen, previousMigrator) { function runBefore(migrationName) { const idx = getIndex(migrationName); // eslint-disable-line no-use-before-define - runUntilIndex(idx - 1); + runUntilIndex(idx - 1); // eslint-disable-line no-use-before-define } function runIncluding(lastMigrationToRun) { From b78683db5c164f2e363f47efbc5b33c68a3cbf4f Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 4 Feb 2025 06:14:38 +0000 Subject: [PATCH 43/44] reintroduce faster migration --- .../20250204-01-disable-nullable-blob-content-types.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/model/migrations-post-knex/20250204-01-disable-nullable-blob-content-types.js b/lib/model/migrations-post-knex/20250204-01-disable-nullable-blob-content-types.js index eb7bc2c39..9d57cae54 100644 --- a/lib/model/migrations-post-knex/20250204-01-disable-nullable-blob-content-types.js +++ b/lib/model/migrations-post-knex/20250204-01-disable-nullable-blob-content-types.js @@ -8,8 +8,8 @@ // except according to the terms contained in the LICENSE file. const up = (db) => db.query(` + UPDATE blobs SET "contentType"='application/octet-stream' WHERE "contentType" IS NULL; ALTER TABLE blobs - ALTER COLUMN "contentType" TYPE TEXT USING(COALESCE("contentType", 'application/octet-stream')), ALTER COLUMN "contentType" SET DEFAULT 'application/octet-stream', ALTER COLUMN "contentType" SET NOT NULL `); From 8b3b4db79c27be35380c2c74c05f8552343aee9f Mon Sep 17 00:00:00 2001 From: alxndrsn Date: Tue, 4 Feb 2025 06:31:21 +0000 Subject: [PATCH 44/44] Add review query --- lib/bin/run-migrations.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/bin/run-migrations.js b/lib/bin/run-migrations.js index 09141f4f4..755c6be01 100644 --- a/lib/bin/run-migrations.js +++ b/lib/bin/run-migrations.js @@ -14,6 +14,8 @@ const { pgMigrations } = require('../model/pg-migrator'); try { await withKnex(require('config').get('default.database'))(migrate); await pgMigrations(require('config').get('default.database')); + // REVIEW should the new migrator follow the same function signature? e.g. + // withPg(config)(migrate)? } catch (err) { console.error('Error:', err.message); process.exit(1);