forked from halvardssm/deno-nessie
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAbstractClient.ts
158 lines (125 loc) · 4.77 KB
/
AbstractClient.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import { parsePath } from "../cli/utils.ts";
import { resolve } from "../deps.ts";
import { loggerFn } from "../cli/state.ts";
import { Migration } from "../types.ts";
export type QueryWithString = (string: string) => string;
export type amountMigrateT = number | undefined;
export type amountRollbackT = amountMigrateT | "all";
export type queryT = string | string[];
export type QueryHandler = (query: queryT) => Promise<any>;
export type MigrationFile = {
up: Migration;
down: Migration;
};
export interface ClientI {
migrationFolder: string;
prepare: () => Promise<void>;
close: () => Promise<void>;
migrate: (amount: amountMigrateT) => Promise<void>;
rollback: (amount: amountRollbackT) => Promise<void>;
query: QueryHandler;
setLogger: loggerFn;
}
export interface nessieConfig {
client: ClientI;
}
export class AbstractClient {
static readonly MAX_FILE_NAME_LENGTH = 100;
protected TABLE_MIGRATIONS = "nessie_migrations";
protected COL_FILE_NAME = "file_name";
protected COL_CREATED_AT = "created_at";
protected REGEX_MIGRATION_FILE_NAME = /^\d{10,14}-.+.ts$/;
protected regexFileName = new RegExp(this.REGEX_MIGRATION_FILE_NAME);
protected migrationFiles: Deno.DirEntry[];
protected logger: loggerFn = () => undefined;
migrationFolder: string;
protected QUERY_GET_LATEST =
`SELECT ${this.COL_FILE_NAME} FROM ${this.TABLE_MIGRATIONS} ORDER BY ${this.COL_FILE_NAME} DESC LIMIT 1;`;
protected QUERY_GET_ALL =
`SELECT ${this.COL_FILE_NAME} FROM ${this.TABLE_MIGRATIONS} ORDER BY ${this.COL_FILE_NAME} DESC;`;
protected QUERY_MIGRATION_INSERT: QueryWithString = (fileName) =>
`INSERT INTO ${this.TABLE_MIGRATIONS} (${this.COL_FILE_NAME}) VALUES ('${fileName}');`;
protected QUERY_MIGRATION_DELETE: QueryWithString = (fileName) =>
`DELETE FROM ${this.TABLE_MIGRATIONS} WHERE ${this.COL_FILE_NAME} = '${fileName}';`;
constructor(migrationFolder: string) {
this.migrationFolder = resolve(migrationFolder);
this.migrationFiles = Array.from(Deno.readDirSync(this.migrationFolder));
}
protected async migrate(
amount: amountMigrateT,
latestMigration: string | undefined,
queryHandler: QueryHandler,
) {
this.logger(amount, "Amount pre");
this.filterAndSortFiles(latestMigration);
amount = typeof amount === "number" ? amount : this.migrationFiles.length;
this.logger(latestMigration, "Latest migrations");
if (this.migrationFiles.length > 0) {
this.logger(
this.migrationFiles.map((el) => el.name),
"Filtered and sorted migration files",
);
amount = Math.min(this.migrationFiles.length, amount);
for (let i = 0; i < amount; i++) {
const file = this.migrationFiles[i];
let { up }: MigrationFile = await import(
parsePath(this.migrationFolder, file.name)
);
let query = await up();
if (!query) query = [];
else if (typeof query === "string") query = [query];
query.push(this.QUERY_MIGRATION_INSERT(file.name));
await queryHandler(query);
console.info(`Migrated ${file.name}`);
}
console.info("Migration complete");
} else {
console.info("Nothing to migrate");
}
}
filterAndSortFiles(queryResult: string | undefined): void {
this.migrationFiles = this.migrationFiles
.filter((file: Deno.DirEntry): boolean => {
if (!this.regexFileName.test(file.name)) return false;
if (queryResult === undefined) return true;
return file.name > queryResult;
})
.sort((a, b) => parseInt(a?.name ?? "0") - parseInt(b?.name ?? "0"));
}
async rollback(
amount: amountRollbackT,
allMigrations: string[] | undefined,
queryHandler: QueryHandler,
) {
this.logger(amount, "Amount pre");
amount = typeof amount === "number"
? amount
: (amount === "all" ? (allMigrations?.length ? allMigrations.length : 0)
: 1);
this.logger(amount, "Received amount to rollback");
this.logger(allMigrations, "Files to rollback");
if (allMigrations && allMigrations.length > 0) {
amount = Math.min(allMigrations.length, amount);
for (let i = 0; i < amount; i++) {
const fileName = allMigrations[i];
let { down }: MigrationFile = await import(
parsePath(this.migrationFolder, fileName)
);
let query = await down();
if (!query) query = [];
else if (typeof query === "string") query = [query];
query.push(this.QUERY_MIGRATION_DELETE(fileName));
await queryHandler(query);
console.info(`Rolled back ${fileName}`);
}
} else {
console.info("Nothing to rollback");
}
}
splitAndTrimQueries(query: string) {
return query.split(";").filter((el) => el.trim() !== "");
}
setLogger(fn: loggerFn) {
this.logger = fn;
}
}