Skip to content
This repository has been archived by the owner on Sep 2, 2022. It is now read-only.

Commit

Permalink
chore: office-maker-api migration script
Browse files Browse the repository at this point in the history
  • Loading branch information
ishibashi_y committed Jul 2, 2019
1 parent 45e6dda commit 61432d6
Show file tree
Hide file tree
Showing 4 changed files with 434 additions and 0 deletions.
254 changes: 254 additions & 0 deletions script/migrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
/**
* office-maker-api migration script
*
* This script copies all data in DynamoDB and S3 from one environment to another environment
* It is useful when you create a new environment and need some test data
*
* Example) migrate from `stg` environment to `dev` environment
* ~$ ./migrate.sh stg dev
*
* Remark: if AWS Backup comes to ap-northeast-1 region, you might use it instead.
*/
import * as aws from 'aws-sdk';

const dynamo = new aws.DynamoDB.DocumentClient({
region: 'ap-northeast-1'
});
const s3 = new aws.S3({
region: 'ap-northeast-1'
});

// Split an array into smaller chunks
const chunksOf = <T>(arr: T[], len: number) => {
let chunks: T[][] = [],
i = 0,
n = arr.length;

while (i < n) {
chunks.push(arr.slice(i, (i += len)));
}

return chunks;
};

const scanAndCopyTable = async (sourceTable: string, targetTable: string) => {
const scanResult = await dynamo
.scan({
TableName: sourceTable
})
.promise();

await chunksOf(scanResult.Items, 25).forEach(async (items, index) => {
await dynamo
.batchWrite({
RequestItems: {
[targetTable]: items.map(item => {
return {
PutRequest: {
Item: item
}
};
})
}
})
.promise();

console.info(
`Copying: ${sourceTable} --> ${targetTable}, ${(index + 1) *
25} items done...`
);
});

console.info(`Migration done: ${sourceTable} --> ${targetTable}`);
};

type QueryOption = {
hashKey: {
name: string;
value: string;
};
rangeKey?: {
name: string;
value: string;
};
};

const queryAndCopyTable = async (
sourceTable: string,
targetTable: string,
option: QueryOption
) => {
const queryResult = await dynamo
.query({
TableName: sourceTable,
KeyConditionExpression: option.rangeKey
? '#hash = :hash AND #range = :range'
: '#hash = :hash',
ExpressionAttributeNames: Object.assign(
{
'#hash': option.hashKey.name
},
option.rangeKey && { '#range': option.rangeKey.name }
),
ExpressionAttributeValues: Object.assign(
{
':hash': option.hashKey.value
},
option.rangeKey && { ':range': option.rangeKey.value }
)
})
.promise();

await chunksOf(queryResult.Items, 25).forEach(async (items, index) => {
await dynamo
.batchWrite({
RequestItems: {
[targetTable]: items.map(item => {
return {
PutRequest: {
Item: item
}
};
})
}
})
.promise();

console.info(
`Copying: ${sourceTable} --> ${targetTable}, ${(index + 1) *
25} items done...`
);
});

console.info(`Migration done: ${sourceTable} --> ${targetTable}`);
};

const performCopyTable = (
sourceTable: string,
targetTable: string,
option?: QueryOption
) => {
if (option) {
return queryAndCopyTable(sourceTable, targetTable, option);
} else {
return scanAndCopyTable(sourceTable, targetTable);
}
};

const performCopyS3 = async (
sourceBucket: string,
targetBucket: string,
option?: {
prefix: string;
}
) => {
const listResult = await s3
.listObjectsV2(
Object.assign(
{
Bucket: sourceBucket
},
option && { Prefix: option.prefix }
)
)
.promise();

await listResult.Contents.forEach(async content => {
s3.copyObject({
Bucket: targetBucket,
CopySource: `${sourceBucket}/${content.Key}`,
Key: content.Key
});
});

console.info(`Migration done: ${sourceBucket} --> ${targetBucket}`);
};

if (process.argv.length < 4) {
console.log(
`office-maker-api migration script
Usage:
~$ ts-node migrate.ts stg dev
~$ FLOOR_ID=xxxxxxxx ts-node migrate.ts stg dev # if you want to migrate only a floor`
);
} else {
const source = process.argv[2];
const target = process.argv[3];
if (target == 'prod') {
console.error('Do not write to the prod account. Abort.');
process.exit(1);
}

const copyTable = (name, option?: QueryOption) => {
const tableNameOf = (env, name) => `office-maker-map-${env}_${name}`;
performCopyTable(
tableNameOf(source, name),
tableNameOf(target, name),
option
);
};
const copyS3 = (option?: { prefix: string }) => {
const bucketNameOf = env => `office-maker-storage-${env}`;
performCopyS3(bucketNameOf(source), bucketNameOf(target), option);
};

copyTable('colors');
copyTable('prototypes');
copyTable(
'edit_floors',
process.env.FLOOR_ID && {
hashKey: {
name: 'tenantId',
value: 'worksap.co.jp'
},
rangeKey: {
name: 'id',
value: process.env.FLOOR_ID
}
}
);
copyTable(
'edit_objects',
process.env.FLOOR_ID && {
hashKey: {
name: 'floorId',
value: process.env.FLOOR_ID
}
}
);
copyTable(
'public_floors',
process.env.FLOOR_ID && {
hashKey: {
name: 'tenantId',
value: 'worksap.co.jp'
},
rangeKey: {
name: 'id',
value: process.env.FLOOR_ID
}
}
);
copyTable(
'public_objects',
process.env.FLOOR_ID && {
hashKey: {
name: 'floorId',
value: process.env.FLOOR_ID
}
}
);

copyS3(
process.env.FLOOR_ID && {
prefix: `files/floors/${process.env.FLOOR_ID}`
}
);

copyS3(
process.env.FLOOR_ID && {
prefix: `images/floors/${process.env.FLOOR_ID}`
}
);
}
10 changes: 10 additions & 0 deletions script/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "script",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"@types/node": "^12.0.10",
"aws-sdk": "^2.482.0"
}
}
62 changes: 62 additions & 0 deletions script/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es2015", "dom"], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "incremental": true, /* Enable incremental compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

/* Strict Type-Checking Options */
"strict": false, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */

/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */

/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
"types": ["node"], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */

/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}
}
Loading

0 comments on commit 61432d6

Please sign in to comment.