1- /* eslint-disable security/detect-non-literal-fs-filename */
2- /* eslint-disable unicorn/prefer-top-level-await */
31import fs from 'node:fs' ;
42import path from 'node:path' ;
53import yargs from 'yargs' ;
@@ -8,8 +6,12 @@ import { UserTransferPlan, UserTransferPlanItem } from './utils/types';
86import { print } from './utils/log-utils' ;
97import { UserTransfer } from './utils/user-transfer' ;
108import { getTemplates } from './utils/ddb-utils' ;
11- import { backupBucketName , backupData } from './utils/backup-utils' ;
12- import { transferFileToNewBucket , writeFile } from './utils/s3-utils' ;
9+ import { backupBucketName , backupData , writeLocal } from './utils/backup-utils' ;
10+ import {
11+ transferFileToNewBucket ,
12+ writeFile as writeRemote ,
13+ } from './utils/s3-utils' ;
14+ import { AttributeValue } from '@aws-sdk/client-dynamodb' ;
1315
1416const params = yargs ( hideBin ( process . argv ) )
1517 . options ( {
@@ -28,41 +30,30 @@ const params = yargs(hideBin(process.argv))
2830 } )
2931 . parseSync ( ) ;
3032
31- function writeLocal ( filename : string , data : string ) {
32- fs . writeFile ( filename , data , ( err ) => {
33- if ( err ) {
34- console . log ( `Error writing file: ${ filename } ` , err ) ;
35- } else {
36- console . log ( `Successfully wrote ${ filename } ` ) ;
37- }
38- } ) ;
39- }
40-
41- async function backup (
33+ async function loadTemplates (
4234 tableName : string ,
43- sourceBucket : string ,
44- backupBucket : string ,
4535 migrations : UserTransferPlanItem [ ]
4636) {
4737 const keys = migrations . map ( ( r ) => ( {
4838 id : r . templateId ,
4939 owner : r . ddb . owner . from ,
5040 } ) ) ;
5141
52- const files = migrations . flatMap ( ( r ) => r . s3 . files . flatMap ( ( a ) => a . from ) ) ;
53-
54- const data = await getTemplates ( tableName , keys ) ;
42+ return await getTemplates ( tableName , keys ) ;
43+ }
5544
56- if ( data . length !== migrations . length ) {
57- throw new Error (
58- `Only retrieved ${ data . length } /${ migrations . length } migrations`
59- ) ;
60- }
45+ async function backup (
46+ templates : Record < string , AttributeValue > [ ] ,
47+ sourceBucket : string ,
48+ backupBucket : string ,
49+ migrations : UserTransferPlanItem [ ]
50+ ) {
51+ const files = migrations . flatMap ( ( r ) => r . s3 . files . flatMap ( ( a ) => a . from ) ) ;
6152
6253 const { name } = path . parse ( params . file ) ;
6354
6455 await backupData (
65- data ,
56+ templates ,
6657 backupBucket ,
6758 `ownership-transfer/${ params . environment } /${ name } `
6859 ) ;
@@ -79,10 +70,33 @@ async function backup(
7970 ) ;
8071}
8172
73+ function assertMatchingTemplates (
74+ fetchedTemplateIds : string [ ] ,
75+ migrationTemplateIds : string [ ]
76+ ) {
77+ const left = new Set ( fetchedTemplateIds ) ;
78+ const right = new Set ( migrationTemplateIds ) ;
79+
80+ if ( left . size !== right . size ) {
81+ throw new Error (
82+ `Mismatch in length of fetched templates and templates to migrate`
83+ ) ;
84+ }
85+
86+ for ( const value of left ) {
87+ if ( ! right . has ( value ) ) {
88+ throw new Error (
89+ `Value "${ value } " found in first array but not in second`
90+ ) ;
91+ }
92+ }
93+ }
94+
8295async function migrate ( ) {
8396 const backupBucket = await backupBucketName ( ) ;
8497
8598 const input = JSON . parse (
99+ // eslint-disable-next-line security/detect-non-literal-fs-filename
86100 fs . readFileSync ( params . file , 'utf8' )
87101 ) as UserTransferPlan ;
88102
@@ -94,26 +108,47 @@ async function migrate() {
94108
95109 print ( `Total migrations: ${ migrations . length } ` ) ;
96110
111+ const templates = await loadTemplates ( input . tableName , migrations ) ;
112+
113+ assertMatchingTemplates (
114+ templates . map ( ( r ) => r . id . S ! ) ,
115+ migrations . map ( ( r ) => r . templateId )
116+ ) ;
117+
97118 if ( ! params . dryRun ) {
98- await backup ( input . tableName , input . bucketName , backupBucket , migrations ) ;
119+ await backup ( templates , input . bucketName , backupBucket , migrations ) ;
120+ print ( 'Data backed up' ) ;
99121 }
100122
101- for ( let i = 0 ; i < migrations . length ; i ++ ) {
102- const migration = migrations [ i ] ;
123+ let count = 0 ;
124+
125+ for ( const migration of migrations ) {
126+ count += 1 ;
127+
128+ print ( `Progress: ${ count } /${ migrations . length } ` ) ;
129+ print ( `Processing: ${ migration . templateId } ` ) ;
130+
131+ const template = templates . find ( ( r ) => r . id . S === migration . templateId ) ;
132+
133+ if ( ! template ) {
134+ print (
135+ `Skipping: Unable to find template ${ migration . templateId } in backup data`
136+ ) ;
137+ continue ;
138+ }
103139
104140 const idx = output . migrate . plans . findIndex (
105141 ( r ) => r . templateId === migration . templateId
106142 ) ;
107143
108144 if ( idx === - 1 ) {
109- print ( `Skipping: Unable to locate index for ${ migration . templateId } ` ) ;
145+ print (
146+ `Skipping: Unable to locate index in output data for ${ migration . templateId } `
147+ ) ;
110148 continue ;
111149 }
112150
113- print ( `Progress: ${ i + 1 } /${ migrations . length } ` ) ;
114- print ( `Processing: ${ migration . templateId } ` ) ;
115-
116- const result = await UserTransfer . apply ( migration , {
151+ const result = await UserTransfer . apply ( migration , template , {
117152 bucketName : input . bucketName ,
118153 tableName : input . tableName ,
119154 dryRun : params . dryRun ,
@@ -130,20 +165,21 @@ async function migrate() {
130165 }
131166
132167 const { dir, name, ext } = path . parse ( params . file ) ;
133-
134- const fileName = `${ name } -${ params . dryRun ? 'dryrun' : '' } ${ ext } ` ;
135-
168+ const filename = `${ name } -${ params . dryRun ? 'dryrun' : 'run' } ${ ext } ` ;
136169 const data = JSON . stringify ( output ) ;
137170
138- const filePath = `ownership-transfer/${ params . environment } /${ name } /${ fileName } ` ;
139-
140- writeLocal ( path . join ( dir , fileName ) , data ) ;
171+ writeLocal ( path . join ( dir , filename ) , data ) ;
141172
142- await writeFile ( filePath , data , backupBucket ) ;
173+ await writeRemote (
174+ `ownership-transfer/${ params . environment } /${ name } /${ filename } ` ,
175+ data ,
176+ backupBucket
177+ ) ;
143178
144- print ( `Plan written to s3:// ${ backupBucket } / ${ filePath } ` ) ;
179+ print ( `Results written to ${ filename } ` ) ;
145180}
146181
147182migrate ( )
148183 . then ( ( ) => console . log ( 'finished' ) )
184+ // eslint-disable-next-line unicorn/prefer-top-level-await
149185 . catch ( ( error ) => console . error ( error ) ) ;
0 commit comments