Skip to content

Commit de9ae61

Browse files
bhansell1chris-elliott-nhsd
authored andcommitted
CCM-10429: backup data
1 parent f6653a3 commit de9ae61

File tree

14 files changed

+319
-672
lines changed

14 files changed

+319
-672
lines changed

data-migration/user-transfer/src/__tests__/utils/backup-utils.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { backupData } from '@/src/utils/backup-utils';
22
import { getAccountId } from '@/src/utils/sts-utils';
3-
import { writeJsonToFile } from '@/src/utils/s3-utils';
3+
import { writeFile } from '@/src/utils/s3-utils';
44

55
const mockItem1 = {
66
templateType: {
@@ -45,7 +45,7 @@ describe('backup-utils', () => {
4545
jest
4646
.mocked(getAccountId)
4747
.mockImplementation(() => Promise.resolve('000000000000'));
48-
const mockedWriteJsonToFile = jest.mocked(writeJsonToFile);
48+
const mockedWriteJsonToFile = jest.mocked(writeFile);
4949

5050
const testParameters = {
5151
destinationOwner: 'def-456',
@@ -73,7 +73,7 @@ describe('backup-utils', () => {
7373

7474
test('should no-op the backup when no items have been found', async () => {
7575
// arrange
76-
const mockedWriteJsonToFile = jest.mocked(writeJsonToFile);
76+
const mockedWriteJsonToFile = jest.mocked(writeFile);
7777

7878
const testParameters = {
7979
destinationOwner: 'def-456',

data-migration/user-transfer/src/__tests__/utils/s3-utils.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { writeJsonToFile } from '@/src/utils/s3-utils';
1+
import { writeFile } from '@/src/utils/s3-utils';
22
import { S3Client } from '@aws-sdk/client-s3';
33

44
jest.mock('@aws-sdk/client-s3', () => ({
@@ -17,7 +17,7 @@ describe('s3-utils', () => {
1717
const testFilePath = '/test/file/path.json';
1818

1919
// act
20-
await writeJsonToFile(testFilePath, testContent, testBucketName);
20+
await writeFile(testFilePath, testContent, testBucketName);
2121

2222
// assert
2323
expect(sendSpy).toHaveBeenCalledTimes(1);

data-migration/user-transfer/src/handler.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.

data-migration/user-transfer/src/migrate.ts

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,23 @@ import fs from 'node:fs';
44
import path from 'node:path';
55
import yargs from 'yargs';
66
import { hideBin } from 'yargs/helpers';
7-
import { MigrationPlan } from './utils/constants';
8-
import { print } from './utils/log';
9-
import { MigrationHandler } from './utils/migration-handler';
7+
import { UserTransferPlan, UserTransferPlanItem } from './utils/types';
8+
import { print } from './utils/log-utils';
9+
import { UserTransfer } from './utils/user-transfer';
10+
import { getTemplates } from './utils/ddb-utils';
11+
import { backupBucketName, backupData } from './utils/backup-utils';
12+
import { transferFileToNewBucket, writeFile } from './utils/s3-utils';
1013

1114
const params = yargs(hideBin(process.argv))
1215
.options({
1316
file: {
1417
type: 'string',
1518
demandOption: true,
1619
},
20+
environment: {
21+
type: 'string',
22+
demandOption: true,
23+
},
1724
dryRun: {
1825
type: 'boolean',
1926
default: true,
@@ -31,20 +38,66 @@ function writeLocal(filename: string, data: string) {
3138
});
3239
}
3340

41+
async function backup(
42+
tableName: string,
43+
sourceBucket: string,
44+
backupBucket: string,
45+
migrations: UserTransferPlanItem[]
46+
) {
47+
const keys = migrations.map((r) => ({
48+
id: r.templateId,
49+
owner: r.ddb.owner.from,
50+
}));
51+
52+
const files = migrations.flatMap((r) => r.s3.files.flatMap((a) => a.from));
53+
54+
const data = await getTemplates(tableName, keys);
55+
56+
if (data.length !== migrations.length) {
57+
throw new Error(
58+
`Only retrieved ${data.length}/${migrations.length} migrations`
59+
);
60+
}
61+
62+
const { name } = path.parse(params.file);
63+
64+
await backupData(
65+
data,
66+
backupBucket,
67+
`ownership-transfer/${params.environment}/${name}`
68+
);
69+
70+
await Promise.all(
71+
files.map((file) =>
72+
transferFileToNewBucket(
73+
sourceBucket,
74+
backupBucket,
75+
file,
76+
`ownership-transfer/${params.environment}/${name}/${file}`
77+
)
78+
)
79+
);
80+
}
81+
3482
async function migrate() {
83+
const backupBucket = await backupBucketName();
84+
3585
const input = JSON.parse(
3686
fs.readFileSync(params.file, 'utf8')
37-
) as MigrationPlan;
87+
) as UserTransferPlan;
3888

39-
const output: MigrationPlan = {
89+
const output: UserTransferPlan = {
4090
...input,
41-
run: input.run + 1,
4291
};
4392

4493
const migrations = input.migrate.plans.filter((r) => r.status === 'migrate');
4594

4695
print(`Total migrations: ${migrations.length}`);
4796

97+
if (!params.dryRun) {
98+
await backup(input.tableName, input.bucketName, backupBucket, migrations);
99+
}
100+
48101
for (let i = 0; i < migrations.length; i++) {
49102
const migration = migrations[i];
50103

@@ -53,14 +106,14 @@ async function migrate() {
53106
);
54107

55108
if (idx === -1) {
56-
print('Skipping: Unable to locate index for ${templateId}');
109+
print(`Skipping: Unable to locate index for ${migration.templateId}`);
57110
continue;
58111
}
59112

60113
print(`Progress: ${i + 1}/${migrations.length}`);
61114
print(`Processing: ${migration.templateId}`);
62115

63-
const result = await MigrationHandler.apply(migration, {
116+
const result = await UserTransfer.apply(migration, {
64117
bucketName: input.bucketName,
65118
tableName: input.tableName,
66119
dryRun: params.dryRun,
@@ -78,11 +131,17 @@ async function migrate() {
78131

79132
const { dir, name, ext } = path.parse(params.file);
80133

81-
const fileName = `${name}-${params.dryRun ? 'dryrun-' : 'run-'}${output.run}${ext}`;
134+
const fileName = `${name}-${params.dryRun ? 'dryrun' : ''}${ext}`;
82135

83136
const data = JSON.stringify(output);
84137

138+
const filePath = `ownership-transfer/${params.environment}/${name}/${fileName}`;
139+
85140
writeLocal(path.join(dir, fileName), data);
141+
142+
await writeFile(filePath, data, backupBucket);
143+
144+
print(`Plan written to s3://${backupBucket}/${filePath}`);
86145
}
87146

88147
migrate()

data-migration/user-transfer/src/plan.ts

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
/* eslint-disable unicorn/no-process-exit */
22
/* eslint-disable unicorn/prefer-top-level-await */
33
import { CognitoRepository } from './utils/cognito-repository';
4-
import { retrieveAllTemplatesV2 } from './utils/ddb-utils';
5-
import {
6-
listItemObjectsWithPaginator,
7-
writeJsonToFile as writeRemote,
8-
} from './utils/s3-utils';
9-
import { MigrationHandler } from './utils/migration-handler';
4+
import { listAllTemplates } from './utils/ddb-utils';
5+
import { listAllFiles, writeFile } from './utils/s3-utils';
6+
import { UserTransfer } from './utils/user-transfer';
107
import yargs from 'yargs';
118
import { hideBin } from 'yargs/helpers';
129
import { getAccountId } from './utils/sts-utils';
13-
import { print } from './utils/log';
10+
import { print } from './utils/log-utils';
11+
import { backupBucketName } from './utils/backup-utils';
1412

1513
const params = yargs(hideBin(process.argv))
1614
.options({
@@ -52,7 +50,7 @@ const params = yargs(hideBin(process.argv))
5250
})
5351
.parseSync();
5452

55-
const cognitoCrawler = new CognitoRepository(
53+
const cognitoRepository = new CognitoRepository(
5654
params.userPoolId,
5755
params.component === 'app'
5856
? {
@@ -65,12 +63,13 @@ const cognitoCrawler = new CognitoRepository(
6563

6664
async function getConfig() {
6765
const accountId = await getAccountId();
66+
const backupBucket = await backupBucketName();
6867

6968
return {
7069
accountId,
7170
templateTableName: `nhs-notify-${params.environment}-${params.component}-api-templates`,
7271
templatesS3InternalBucketName: `nhs-notify-${accountId}-eu-west-2-${params.environment}-${params.component}-internal`,
73-
templatesS3BackupBucketName: `nhs-notify-${accountId}-eu-west-2-main-acct-migration-backup`,
72+
templatesS3BackupBucketName: backupBucket,
7473
};
7574
}
7675

@@ -82,15 +81,13 @@ async function plan() {
8281
templatesS3BackupBucketName,
8382
} = await getConfig();
8483

85-
const users = await cognitoCrawler.getAllUsers();
84+
const users = await cognitoRepository.getAllUsers();
8685

87-
const templates = await retrieveAllTemplatesV2(templateTableName);
86+
const templates = await listAllTemplates(templateTableName);
8887

89-
const files = await listItemObjectsWithPaginator(
90-
templatesS3InternalBucketName
91-
);
88+
const files = await listAllFiles(templatesS3InternalBucketName);
9289

93-
const transferPlan = await MigrationHandler.plan(
90+
const transferPlan = await UserTransfer.plan(
9491
users,
9592
{
9693
tableName: templateTableName,
@@ -102,13 +99,13 @@ async function plan() {
10299
}
103100
);
104101

105-
const fileName = `transfer-plan-${accountId}-${params.environment}-${params.component}-${Date.now()}.json`;
102+
const fileName = `transfer-plan-${accountId}-${params.environment}-${params.component}-${Date.now()}`;
106103

107104
const data = JSON.stringify(transferPlan);
108105

109-
const filePath = `ownership-transfer/${params.environment}/${fileName}`;
106+
const filePath = `ownership-transfer/${params.environment}/${fileName}/${fileName}.json`;
110107

111-
await writeRemote(filePath, data, templatesS3BackupBucketName);
108+
await writeFile(filePath, data, templatesS3BackupBucketName);
112109

113110
print(`Plan written to s3://${templatesS3InternalBucketName}/${filePath}`);
114111
}

data-migration/user-transfer/src/user-transfer.ts

Lines changed: 0 additions & 128 deletions
This file was deleted.

0 commit comments

Comments
 (0)