diff --git a/backend/migrations/1579387929122-foo.js b/backend/migrations/1579387929122-foo.js new file mode 100644 index 000000000..9d23051ac --- /dev/null +++ b/backend/migrations/1579387929122-foo.js @@ -0,0 +1,69 @@ +import { throwError, of, concat } from 'rxjs' +import { tap, flatMap, mergeMap, map, catchError, filter } from 'rxjs/operators' +import CONFIG from '../src/config' +import { getNeode, getDriver } from '../src/bootstrap/neo4j' +import normalizeEmail from '../src/schema/resolvers//helpers/normalizeEmail' + + +export function up (next) { + const driver = getDriver() + const rxSession = driver.rxSession() + rxSession + .beginTransaction() + .pipe( + flatMap(txc => + concat( + txc + .run("MATCH (email:EmailAddress) RETURN email {.email}") + .records() + .pipe( + map(record => { + const { email } = record.get('email') + const normalizedEmail = normalizeEmail(email) + return { email, normalizedEmail } + }), + filter(({email, normalizedEmail}) => email !== normalizedEmail), + mergeMap(({email, normalizedEmail})=> { + return txc + .run(` + MATCH (oldUser:User)-[:PRIMARY_EMAIL]->(oldEmail:EmailAddress {email: $email}), (oldUser)-[previousRelationship]-(oldEmail) + MATCH (user:User)-[:PRIMARY_EMAIL]->(email:EmailAddress {email: $normalizedEmail}) + DELETE previousRelationship + WITH oldUser, oldEmail, user, email + CALL apoc.refactor.mergeNodes([user, oldUser], { properties: 'discard', mergeRels: true }) YIELD node as mergedUser + CALL apoc.refactor.mergeNodes([email, oldEmail], { properties: 'discard', mergeRels: true }) YIELD node as mergedEmail + RETURN user {.*}, email {.*} + `, { email, normalizedEmail }) + .records() + .pipe( + map(r => ({ + oldEmail: email, + email: r.get('email'), + user: r.get('user'), + })) + ) + }), + ), + txc.commit(), + ).pipe(catchError(err => txc.rollback().pipe(throwError(err)))) + ) + ) + .subscribe({ + next: ({ user, email, oldUser, oldEmail }) => console.log(` + Merged: + ============================= + userId: ${user.id} + email: ${oldEmail} => ${email.email} + ============================= + `), + complete: () => { + console.log('Merging of duplicate users completed') + next() + }, + error: error => throw new Error(error) + }) +} + +export function down () { + throw new Error("Irreversible migration") +} diff --git a/backend/package.json b/backend/package.json index acd0a9549..4d23406c7 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,8 +12,9 @@ "test": "jest --forceExit --detectOpenHandles --runInBand", "db:reset": "babel-node src/seed/reset-db.js", "db:seed": "babel-node src/seed/seed-db.js", - "db:setup": "babel-node src/migration/setup.js", - "db:migrate": "babel-node src/migration/migrate.js" + "db:setup": "babel-node src/db/setup.js", + "db:migrate:create": "migrate create --template-file./src/db/migrationTemplate.js", + "db:migrate": "migrate --compiler 'js:@babel/register'" }, "author": "Human Connection gGmbH", "license": "MIT", @@ -80,6 +81,7 @@ "metascraper-url": "^5.10.3", "metascraper-video": "^5.10.3", "metascraper-youtube": "^5.10.5", + "migrate": "^1.6.2", "minimatch": "^3.0.4", "mustache": "^4.0.0", "neo4j-driver": "^4.0.1", diff --git a/backend/src/db/migrationTemplate.js b/backend/src/db/migrationTemplate.js new file mode 100644 index 000000000..dff6564ef --- /dev/null +++ b/backend/src/db/migrationTemplate.js @@ -0,0 +1,7 @@ +export function up (next) { + next() +} + +export function down (next) { + next() +} diff --git a/backend/src/migration/setup.js b/backend/src/db/setup.sj similarity index 100% rename from backend/src/migration/setup.js rename to backend/src/db/setup.sj diff --git a/backend/src/migration/migrate.js b/backend/src/migration/migrate.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/backend/yarn.lock b/backend/yarn.lock index 8c67f5a30..db1062af0 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1680,6 +1680,11 @@ ansi-regex@^4.0.0, ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + ansi-styles@^3.1.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -2492,6 +2497,17 @@ chalk@2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -2704,6 +2720,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +commander@^2.9.0, commander@~2.20.3: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + commander@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" @@ -2714,11 +2735,6 @@ commander@^4.0.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.0.1.tgz#b67622721785993182e807f4883633e6401ba53c" integrity sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA== -commander@~2.20.3: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -3001,6 +3017,11 @@ date-fns@2.9.0: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.9.0.tgz#d0b175a5c37ed5f17b97e2272bbc1fa5aec677d2" integrity sha512-khbFLu/MlzLjEzy9Gh8oY1hNt/Dvxw3J6Rbc28cVoYWQaC1S3YI4xwkF9ZWcjDLscbZlY9hISMr66RFzZagLsA== +dateformat@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" + integrity sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI= + dayjs@^1.8.19: version "1.8.19" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.19.tgz#5117dc390d8f8e586d53891dbff3fa308f51abfe" @@ -3423,7 +3444,7 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -3834,7 +3855,6 @@ extsprintf@^1.2.0: faker@Marak/faker.js#master: version "4.1.0" - uid "3b2fa4aebccee52ae1bafc15d575061fb30c3cf1" resolved "https://codeload.github.com/Marak/faker.js/tar.gz/3b2fa4aebccee52ae1bafc15d575061fb30c3cf1" fast-deep-equal@^2.0.1: @@ -4387,6 +4407,13 @@ har-validator@~5.1.0: ajv "^6.5.5" har-schema "^2.0.0" +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" @@ -6119,6 +6146,20 @@ micromatch@^4.0.2: braces "^3.0.1" picomatch "^2.0.5" +migrate@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/migrate/-/migrate-1.6.2.tgz#8970d596780553fe9f545bdf83806df8473f025b" + integrity sha512-XAFab+ArPTo9BHzmihKjsZ5THKRryenA+lwob0R+ax0hLDs7YzJFJT5YZE3gtntZgzdgcuFLs82EJFB/Dssr+g== + dependencies: + chalk "^1.1.3" + commander "^2.9.0" + dateformat "^2.0.0" + dotenv "^4.0.0" + inherits "^2.0.3" + minimatch "^3.0.3" + mkdirp "^0.5.1" + slug "^0.9.2" + mime-db@1.40.0: version "1.40.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" @@ -6163,7 +6204,7 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -minimatch@^3.0.4: +minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -7704,6 +7745,11 @@ serve-static@1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -7784,6 +7830,13 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +slug@^0.9.2: + version "0.9.4" + resolved "https://registry.yarnpkg.com/slug/-/slug-0.9.4.tgz#fad5f1ef33150830c7688cd8500514576eccabd8" + integrity sha512-3YHq0TeJ4+AIFbJm+4UWSQs5A1mmeWOTQqydW3OoPmQfNKxlO96NDRTIrp+TBkmvEsEFrd+Z/LXw8OD/6OlZ5g== + dependencies: + unicode ">= 0.3.1" + slug@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slug/-/slug-2.1.0.tgz#293f8d53de7e55c15871846fd1bc36114841a8c7" @@ -8151,6 +8204,11 @@ supertest@~4.0.2: methods "^1.1.2" superagent "^3.8.3" +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + supports-color@^4.0.0: version "4.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b"