Skip to content

Commit

Permalink
feat: Add migration script for phone number normalisation
Browse files Browse the repository at this point in the history
  • Loading branch information
anku255 committed Nov 17, 2023
1 parent f93d7e2 commit c32dd06
Show file tree
Hide file tree
Showing 5 changed files with 662 additions and 1 deletion.
21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,26 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [7.0.12] - 2023-11-16

- Adds Phone Number normalisation
In this release, the core API routes have been updated to incorporate phone number normalization before processing. Consequently, existing entries in the database also need to undergo normalization. To facilitate this, we have included a migration script to normalize phone numbers for all the existing entries.

**NOTE**: You can skip the migration if you are not using passwordless via phone number.

### Migration steps

1. Ensure that the core is already upgraded to version 7.0.12 (CDI version 4.0)
2. Run the migration script

<details>
<summary>Steps</summary>

Make sure your Node.js version is 16 or above to run the script. Locate the migration script at `supertokens-core/migration_scripts/to_version_7_1/index.js`. Modify the script by updating the `DB_HOST`, `DB_USER`, `DB_PASSWORD`, and `DB_NAME` variables with the correct values. Subsequently, run the following commands to initiate the script:

```bash
$ cd supertokens-core/migration_scripts/to_version_7_1
$ npm install
$ npm start
```
</details>

## [7.0.11] - 2023-11-10

Expand Down
1 change: 1 addition & 0 deletions migration_scripts/to_version_7_1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
118 changes: 118 additions & 0 deletions migration_scripts/to_version_7_1/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
*
* You may not use this file except in compliance with the License. You may
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

const libphonenumber = require('libphonenumber-js/max');

// Update the following credentials before running the script
const DB_HOST = "";
const DB_USER = "";
const DB_PASSWORD = "";
const DB_NAME = "";
const CLIENT = ""; // Use "pg" for PostgreSQL and "mysql2" for MySQL DB

if(!DB_HOST || !CLIENT) {
console.error('Please update the DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASE and CLIENT variables before running the script.');
return;
}

const knex = require('knex')({
client: CLIENT,
connection: {
host: DB_HOST,
user: DB_USER,
password: DB_PASSWORD,
database: DB_NAME,
},
});

function getUpdateQuery(table, entry, normalizedPhoneNumber) {
let query = `UPDATE ${table} SET phone_number = ? `;

if (table === 'passwordless_users') {
query += 'WHERE app_id = ? AND user_id = ?'
return knex.raw(query, [normalizedPhoneNumber, entry.app_id, entry.user_id]);
}

if (table === 'passwordless_devices') {
query += 'WHERE app_id = ? AND tenant_id = ? AND device_id_hash = ?'
return knex.raw(query, [normalizedPhoneNumber, entry.app_id, entry.tenant_id, entry.device_id_hash]);
}

if (table === 'passwordless_user_to_tenant') {
query += 'WHERE app_id = ? AND tenant_id = ? AND user_id = ?'
return knex.raw(query, [normalizedPhoneNumber, entry.app_id, entry.tenant_id, entry.user_id]);
}

throw new Error(`Invalid table name: ${table}`);
}

function getNormalizedPhoneNumber(phoneNumber) {
try {
return libphonenumber.parsePhoneNumber(phoneNumber, { extract: false }).format('E.164');
} catch (error) {
return null;
}
}

async function updatePhoneNumbers(table) {
const batchSize = 1000;
let offset = 0;

try {
while (true) {
const entries = await knex.raw(`SELECT * FROM ${table} WHERE phone_number is NOT NULL LIMIT ${batchSize} OFFSET ${offset}`);
const rows = entries.rows ? entries.rows : entries[0];

const batchUpdates = [];

for (const entry of rows) {
const currentPhoneNumber = entry.phone_number;
const normalizedPhoneNumber = getNormalizedPhoneNumber(currentPhoneNumber);

if (normalizedPhoneNumber && normalizedPhoneNumber !== currentPhoneNumber) {
const updateQuery = getUpdateQuery(table, entry, normalizedPhoneNumber);
batchUpdates.push(updateQuery);
}
}

await Promise.all(batchUpdates);

offset += rows.length;

console.log(`Processed ${offset} rows for table ${table}`);

if (rows.length < batchSize) {
break;
}
}
} catch (error) {
console.error(`Error normalising phone numbers for table ${table}:`, error.message);
}
}

async function runScript() {
const tables = ['passwordless_users', 'passwordless_devices', 'passwordless_user_to_tenant'];

for (const table of tables) {
await updatePhoneNumbers(table);
}

console.log('Finished normalising phone numbers!');

knex.destroy();
}

runScript();
Loading

0 comments on commit c32dd06

Please sign in to comment.