Skip to content

Commit 540a3d9

Browse files
committed
script
1 parent b7ffba3 commit 540a3d9

File tree

9 files changed

+7369
-4649
lines changed

9 files changed

+7369
-4649
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"extends": [
3+
"../../.eslintrc"
4+
]
5+
}

data-migration/campaign/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npm run transfer -- --file ./ids.txt --environment alnu1 --clientId c79e4383-e3f3-4a85-b019-653df868f9eb --newCampaignId new --supplier WTMMOCK

data-migration/campaign/ids.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
21ae04b7-bc13-4b70-bdc1-7a3675636dbb
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* For a detailed explanation regarding each configuration property, visit:
3+
* https://jestjs.io/docs/configuration
4+
*/
5+
6+
import type { Config } from 'jest';
7+
import { pathsToModuleNameMapper } from 'ts-jest';
8+
import { compilerOptions } from './tsconfig.json';
9+
10+
const config: Config = {
11+
preset: 'ts-jest',
12+
13+
// Automatically clear mock calls, instances, contexts and results before every test
14+
clearMocks: true,
15+
16+
// Indicates whether the coverage information should be collected while executing the test
17+
collectCoverage: true,
18+
19+
// The directory where Jest should output its coverage files
20+
coverageDirectory: './.reports/unit/coverage',
21+
22+
// Indicates which provider should be used to instrument code for coverage
23+
coverageProvider: 'v8',
24+
25+
coverageThreshold: {
26+
global: {
27+
branches: 100,
28+
functions: 100,
29+
lines: 100,
30+
statements: -10,
31+
},
32+
},
33+
34+
collectCoverageFrom: ['src/**/*.ts*'],
35+
36+
coveragePathIgnorePatterns: ['handler.ts', 'constants.ts'],
37+
38+
// Set the absolute path for imports
39+
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
40+
prefix: '<rootDir>/',
41+
}),
42+
43+
// Use this configuration option to add custom reporters to Jest
44+
reporters: [
45+
'default',
46+
[
47+
'../../node_modules/jest-html-reporter',
48+
{
49+
pageTitle: 'Test Report',
50+
outputPath: './.reports/unit/test-report.html',
51+
includeFailureMsg: true,
52+
},
53+
],
54+
],
55+
56+
// The test environment that will be used for testing
57+
testEnvironment: 'node',
58+
59+
testPathIgnorePatterns: ['/node_modules/', '/tests/'],
60+
};
61+
62+
export default config;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"dependencies": {
3+
"@aws-sdk/client-dynamodb": "^3.864.0",
4+
"@aws-sdk/client-s3": "^3.864.0",
5+
"@aws-sdk/client-sts": "^3.864.0",
6+
"@aws-sdk/lib-dynamodb": "^3.864.0",
7+
"@aws-sdk/client-sqs": "^3.864.0",
8+
"yargs": "^17.7.2",
9+
"zod": "4.0.17",
10+
"nhs-notify-backend-client": "0.0.1"
11+
},
12+
"description": "",
13+
"devDependencies": {
14+
"@aws-sdk/types": "^3.862.0",
15+
"@tsconfig/node20": "^20.1.5",
16+
"@types/node": "^20.17.32",
17+
"jest": "^29.7.0",
18+
"jest-html-reporter": "^3.10.2",
19+
"jest-mock-extended": "^3.0.7",
20+
"typescript": "^5.8.2"
21+
},
22+
"main": "src/handler.ts",
23+
"name": "campaign-transfer",
24+
"scripts": {
25+
"lint": "eslint ./src",
26+
"lint:fix": "eslint ./src --fix",
27+
"test:unit": "jest",
28+
"transfer": "tsx src/script.ts",
29+
"typecheck": "tsc --noEmit"
30+
}
31+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import fs from 'node:fs';
2+
import process from 'node:process';
3+
import yargs from 'yargs/yargs';
4+
import { hideBin } from 'yargs/helpers';
5+
import {
6+
DynamoDBDocumentClient,
7+
UpdateCommand,
8+
UpdateCommandInput,
9+
} from '@aws-sdk/lib-dynamodb';
10+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
11+
import { SendMessageCommand, SQSClient } from '@aws-sdk/client-sqs';
12+
import { $TemplateDtoSchema } from 'nhs-notify-backend-client';
13+
import { z } from 'zod/v4';
14+
import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';
15+
16+
const ddb = DynamoDBDocumentClient.from(
17+
new DynamoDBClient({ region: 'eu-west-2' }),
18+
{
19+
marshallOptions: { removeUndefinedValues: true },
20+
}
21+
);
22+
23+
const sqs = new SQSClient({ region: 'eu-west-2' });
24+
25+
const sts = new STSClient({ region: 'eu-west-2' });
26+
27+
export async function getAccountId(): Promise<string> {
28+
const callerIdentity = await sts.send(new GetCallerIdentityCommand());
29+
const accountId = callerIdentity.Account;
30+
if (!accountId) {
31+
throw new Error('Unable to get account ID from caller');
32+
}
33+
return accountId;
34+
}
35+
36+
const { environment, file, newCampaignId, clientId, supplier } = yargs(
37+
hideBin(process.argv)
38+
)
39+
.options({
40+
environment: {
41+
type: 'string',
42+
demandOption: true,
43+
},
44+
clientId: {
45+
type: 'string',
46+
demandOption: true,
47+
},
48+
file: {
49+
type: 'string',
50+
demandOption: true,
51+
},
52+
newCampaignId: {
53+
type: 'string',
54+
demandOption: true,
55+
},
56+
supplier: {
57+
type: 'string',
58+
default: 'MBA',
59+
},
60+
})
61+
.parseSync();
62+
63+
async function main() {
64+
const acct = await getAccountId();
65+
66+
// eslint-disable-next-line security/detect-non-literal-fs-filename
67+
const templateIds = fs
68+
.readFileSync(file, 'utf8')
69+
.split('\n')
70+
.map((s) => s.trim())
71+
.filter(Boolean);
72+
73+
for (const id of templateIds) {
74+
const updateInput: UpdateCommandInput = {
75+
TableName: `nhs-notify-${environment}-app-api-templates`,
76+
Key: {
77+
id,
78+
owner: `CLIENT#${clientId}`,
79+
},
80+
UpdateExpression:
81+
'SET templateStatus = :waitingForProofProofStatus, files.proofs = :emptyObj, campaignId = :newId REMOVE sftpSendLockTime',
82+
ExpressionAttributeValues: {
83+
':newId': newCampaignId,
84+
':pendingProofStatus': 'PENDING_PROOF_REQUEST',
85+
':waitingForProofProofStatus': 'WAITING_FOR_PROOF',
86+
':proofAvailableStatus': 'PROOF_AVAILABLE',
87+
':submittedStatus': 'SUBMITTED',
88+
':emptyObj': {},
89+
},
90+
ConditionExpression:
91+
'attribute_exists(id) AND templateStatus in (:pendingProofStatus, :waitingForProofProofStatus, :submittedStatus, :proofAvailableStatus)',
92+
ReturnValues: 'ALL_NEW',
93+
ReturnValuesOnConditionCheckFailure: 'ALL_OLD',
94+
};
95+
96+
const res = await ddb.send(new UpdateCommand(updateInput));
97+
98+
console.log(`updated ${id}`);
99+
100+
const template = z
101+
.intersection(
102+
$TemplateDtoSchema,
103+
z.object({
104+
templateType: z.literal('LETTER'),
105+
clientId: z.string(),
106+
personalisationParameters: z.array(z.string()),
107+
createdBy: z.string(),
108+
})
109+
)
110+
.parse(res.Attributes);
111+
112+
const proofRequest = {
113+
campaignId: newCampaignId,
114+
language: template.language,
115+
letterType: template.letterType,
116+
pdfVersionId: template.files.pdfTemplate.currentVersion,
117+
personalisationParameters: template.personalisationParameters,
118+
supplier,
119+
templateId: id,
120+
templateName: template.name,
121+
testDataVersionId: template.files.testDataCsv?.currentVersion,
122+
user: { userId: template.createdBy, clientId: template.clientId },
123+
};
124+
125+
await sqs.send(
126+
new SendMessageCommand({
127+
QueueUrl: `https://sqs.eu-west-2.amazonaws.com/${acct}/nhs-notify-${environment}-app-sftp-upload-queue`,
128+
MessageBody: JSON.stringify(proofRequest),
129+
})
130+
);
131+
132+
console.log(`requested proof for ${id}`);
133+
}
134+
}
135+
136+
// eslint-disable-next-line unicorn/prefer-top-level-await
137+
main().catch((error) => {
138+
console.error(error);
139+
// eslint-disable-next-line unicorn/no-process-exit
140+
process.exit(1);
141+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"compilerOptions": {
3+
"paths": {
4+
"@/*": [
5+
"./*"
6+
]
7+
},
8+
"resolveJsonModule": true
9+
},
10+
"extends": "@tsconfig/node20/tsconfig.json",
11+
"include": [
12+
"**/*.ts"
13+
]
14+
}

0 commit comments

Comments
 (0)