Skip to content

Commit 2422189

Browse files
committed
feat: add migrations
1 parent 2b96262 commit 2422189

File tree

6 files changed

+206
-16
lines changed

6 files changed

+206
-16
lines changed

README.md

+17-16
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,28 @@ Deployed on Heroku
1313
Using postgres.
1414
Copy `.env.example` into `.env` and update the variables to match your environment, or define local variable named DATABASE_URL of the form `postgres://user:pass@localhost:5432/bots_db`
1515

16-
Install locally by running:
16+
Install locally:
1717

18-
```bash
19-
$ psql postgres
20-
CREATE ROLE bots WITH LOGIN PASSWORD 'yourpass';
21-
ALTER ROLE bots CREATEDB;
18+
1. Create Database and User:
2219

23-
$ psql postgres -U bots
24-
CREATE DATABASE bots_db;
25-
GRANT ALL PRIVILEGES ON DATABASE bots_db TO bots;
26-
CREATE TYPE platform AS ENUM('TWITTER', 'FACEBOOK', 'INSTAGRAM');
27-
CREATE TYPE reason AS ENUM('BOT', 'VIOLENCE', 'FAKE');
28-
CREATE TYPE status AS ENUM('REPORTED', 'IN_PROCESS', 'BOT', 'NOT_BOT', 'DUPLICATE');
20+
```bash
21+
$ psql postgres
22+
CREATE ROLE bots WITH LOGIN PASSWORD 'yourpass';
23+
ALTER ROLE bots CREATEDB;
2924

30-
DROP TABLE IF EXISTS user_statuses;
31-
CREATE TABLE user_statuses (ID SERIAL PRIMARY KEY, platform platform NOT NULL, user_id VARCHAR(30) NOT NULL, post_id VARCHAR(30) NOT NULL, comment_id VARCHAR(30), reply_comment_id VARCHAR(30), reasons reason[] NOT NULL, status status NOT NULL, description VARCHAR(200), reporter_key VARCHAR(30) NOT NULL, unique (platform, user_id, post_id, comment_id, reply_comment_id));
25+
$ psql postgres -U bots
26+
CREATE DATABASE bots_db;
27+
GRANT ALL PRIVILEGES ON DATABASE bots_db TO bots;
28+
```
3229

33-
DROP TABLE IF EXISTS reporters;
34-
CREATE TABLE reporters (ID SERIAL PRIMARY KEY, platform platform, user_id VARCHAR(30), reporter_key VARCHAR(30) NOT NULL);
30+
2. Run migrations to create tables:
3531

36-
```
32+
```bash
33+
npm run migrate
34+
35+
# to revert the last migration
36+
npm run migrate:revert
37+
```
3738

3839
### Run Example
3940

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
"license": "MIT",
66
"main": "dist/index.js",
77
"scripts": {
8+
"typeorm": "ts-node ./node_modules/typeorm/cli.js",
9+
"migrate": "npm run typeorm -- migration:run",
10+
"migrate:revert": "npm run typeorm -- migration:revert",
811
"jscopy": "copyfiles -u 1 src/**/*.js dist",
912
"build": "tsoa routes && tsc && npm run jscopy",
1013
"start": "node dist/index.js",

src/core/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './symbols';
22
export * from './cache';
33
export * from './logger';
4+
export * from './migrations';

src/core/migrations.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Get the name of an enum, postfixed with '_enum'.
3+
*
4+
* @param name
5+
*/
6+
export function getEnumName(name: string): string {
7+
return `${name}_enum`;
8+
}
9+
10+
/**
11+
* Generate a query to create an enum type in the db.
12+
*
13+
* @param name Name of the enum, in lowercase
14+
* @param enumObject The enum type itself
15+
*/
16+
export function generateEnumQuery(
17+
op: 'drop' | 'create',
18+
name: string,
19+
values?: string[]
20+
): string {
21+
const enumName = getEnumName(name);
22+
23+
if (op === 'drop') {
24+
return `DROP TYPE IF EXISTS ${enumName}`;
25+
}
26+
27+
return `CREATE TYPE ${enumName} AS ENUM ('${values.join("', '")}');`;
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import { MigrationInterface, QueryRunner, Table } from 'typeorm';
2+
3+
import { generateEnumQuery, getEnumName } from '../core';
4+
5+
export class CreateReportersTable1552670893473 implements MigrationInterface {
6+
public async up(queryRunner: QueryRunner): Promise<any> {
7+
await this._createEnums(queryRunner);
8+
await this._createTables(queryRunner);
9+
}
10+
11+
public async down(queryRunner: QueryRunner): Promise<any> {
12+
await queryRunner.dropTable('user_statuses', true, true);
13+
await queryRunner.dropTable('reporters', true, true);
14+
await queryRunner.query(generateEnumQuery('drop', 'platform'));
15+
await queryRunner.query(generateEnumQuery('drop', 'status'));
16+
await queryRunner.query(generateEnumQuery('drop', 'reason'));
17+
}
18+
19+
private async _createEnums(queryRunner: QueryRunner) {
20+
await queryRunner.query(
21+
generateEnumQuery('create', 'platform', ['TWITTER', 'FACEBOOK', 'INSTAGRAM'])
22+
);
23+
await queryRunner.query(
24+
generateEnumQuery('create', 'status', [
25+
'REPORTED',
26+
'IN_PROCESS',
27+
'BOT',
28+
'NOT_BOT',
29+
'DUPLICATE'
30+
])
31+
);
32+
await queryRunner.query(
33+
generateEnumQuery('create', 'reason', ['BOT', 'VIOLENCE', 'FAKE'])
34+
);
35+
}
36+
37+
private async _createTables(queryRunner: QueryRunner) {
38+
await queryRunner.createTable(
39+
new Table({
40+
name: 'reporters',
41+
columns: [
42+
{
43+
name: 'id',
44+
type: 'int',
45+
isPrimary: true,
46+
isGenerated: true,
47+
generationStrategy: 'increment'
48+
},
49+
{
50+
name: 'platform',
51+
type: getEnumName('platform'),
52+
isNullable: false
53+
},
54+
{
55+
name: 'user_id',
56+
type: 'varchar',
57+
length: '30',
58+
isNullable: false
59+
},
60+
{
61+
name: 'reporter_key',
62+
type: 'varchar',
63+
length: '30',
64+
isNullable: false,
65+
isUnique: true
66+
}
67+
],
68+
uniques: [{ columnNames: ['platform', 'user_id'] }]
69+
}),
70+
true
71+
);
72+
73+
await queryRunner.createTable(
74+
new Table({
75+
name: 'user_statuses',
76+
columns: [
77+
{
78+
name: 'id',
79+
type: 'int',
80+
isPrimary: true,
81+
isGenerated: true,
82+
generationStrategy: 'increment'
83+
},
84+
{
85+
name: 'platform',
86+
type: getEnumName('platform'),
87+
isNullable: false
88+
},
89+
{
90+
name: 'user_id',
91+
type: 'varchar',
92+
length: '30',
93+
isNullable: false
94+
},
95+
{
96+
name: 'post_id',
97+
type: 'varchar',
98+
length: '30',
99+
isNullable: false
100+
},
101+
{
102+
name: 'comment_id',
103+
type: 'varchar',
104+
length: '30',
105+
isNullable: true
106+
},
107+
{
108+
name: 'reply_comment_id',
109+
type: 'varchar',
110+
length: '30',
111+
isNullable: true
112+
},
113+
{
114+
name: 'reasons',
115+
type: getEnumName('reason'),
116+
isArray: true,
117+
isNullable: false
118+
},
119+
{
120+
name: 'status',
121+
type: getEnumName('status'),
122+
default: `'REPORTED'`,
123+
isNullable: false
124+
},
125+
{
126+
name: 'description',
127+
type: 'varchar',
128+
length: '200',
129+
isNullable: true
130+
},
131+
{
132+
name: 'reporter_key',
133+
type: 'varchar',
134+
length: '30',
135+
isNullable: false
136+
}
137+
],
138+
uniques: [
139+
{
140+
columnNames: ['platform', 'user_id', 'post_id', 'comment_id', 'reply_comment_id']
141+
}
142+
],
143+
foreignKeys: [
144+
{
145+
columnNames: ['reporter_key'],
146+
referencedColumnNames: ['reporter_key'],
147+
referencedTableName: 'reporters',
148+
onDelete: 'CASCADE'
149+
}
150+
]
151+
}),
152+
true,
153+
true
154+
);
155+
}
156+
}

tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"compilerOptions": {
3+
"baseUrl": ".",
34
"target": "es2018",
45
"module": "commonjs",
56
"outDir": "dist",

0 commit comments

Comments
 (0)