rake-db 2.3.29 → 2.3.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -21
- package/.env +0 -1
- package/.env.local +0 -2
- package/.turbo/turbo-test.log +0 -22
- package/.turbo/turbo-test:ci.log +0 -22
- package/CHANGELOG.md +0 -388
- package/app/dbScript.ts +0 -33
- package/app/migrations/20221017181504_createUser.ts +0 -14
- package/app/migrations/20221017200111_createProfile.ts +0 -10
- package/app/migrations/20221017200252_createChat.ts +0 -9
- package/app/migrations/20221017200326_createChatUser.ts +0 -10
- package/app/migrations/20221017200900_createMessage.ts +0 -12
- package/app/migrations/20221017201235_createGeoSchema.ts +0 -5
- package/app/migrations/20221017210011_createCountry.ts +0 -8
- package/app/migrations/20221017210133_createCity.ts +0 -9
- package/app/migrations/20221105202843_createUniqueTable.ts +0 -12
- package/jest-setup.ts +0 -3
- package/rollup.config.js +0 -3
- package/src/ast.ts +0 -130
- package/src/commands/createOrDrop.test.ts +0 -214
- package/src/commands/createOrDrop.ts +0 -151
- package/src/commands/generate.test.ts +0 -136
- package/src/commands/generate.ts +0 -93
- package/src/commands/migrateOrRollback.test.ts +0 -267
- package/src/commands/migrateOrRollback.ts +0 -190
- package/src/common.test.ts +0 -295
- package/src/common.ts +0 -353
- package/src/errors.ts +0 -3
- package/src/index.ts +0 -8
- package/src/migration/change.test.ts +0 -16
- package/src/migration/change.ts +0 -15
- package/src/migration/changeTable.test.ts +0 -897
- package/src/migration/changeTable.ts +0 -566
- package/src/migration/createTable.test.ts +0 -384
- package/src/migration/createTable.ts +0 -193
- package/src/migration/migration.test.ts +0 -430
- package/src/migration/migration.ts +0 -518
- package/src/migration/migrationUtils.ts +0 -307
- package/src/migration/tableMethods.ts +0 -8
- package/src/pull/astToMigration.test.ts +0 -275
- package/src/pull/astToMigration.ts +0 -173
- package/src/pull/dbStructure.test.ts +0 -180
- package/src/pull/dbStructure.ts +0 -413
- package/src/pull/pull.test.ts +0 -115
- package/src/pull/pull.ts +0 -22
- package/src/pull/structureToAst.test.ts +0 -841
- package/src/pull/structureToAst.ts +0 -372
- package/src/rakeDb.test.ts +0 -131
- package/src/rakeDb.ts +0 -84
- package/src/test-utils.ts +0 -64
- package/tsconfig.json +0 -12
package/src/common.test.ts
DELETED
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createSchemaMigrations,
|
|
3
|
-
getDatabaseAndUserFromOptions,
|
|
4
|
-
getFirstWordAndRest,
|
|
5
|
-
processRakeDbConfig,
|
|
6
|
-
getMigrationFiles,
|
|
7
|
-
getTextAfterTo,
|
|
8
|
-
joinColumns,
|
|
9
|
-
joinWords,
|
|
10
|
-
quoteWithSchema,
|
|
11
|
-
setAdapterOptions,
|
|
12
|
-
setAdminCredentialsToOptions,
|
|
13
|
-
sortAsc,
|
|
14
|
-
sortDesc,
|
|
15
|
-
migrationConfigDefaults,
|
|
16
|
-
} from './common';
|
|
17
|
-
import prompts from 'prompts';
|
|
18
|
-
import { Adapter } from 'pqb';
|
|
19
|
-
import { readdir } from 'fs/promises';
|
|
20
|
-
import path from 'path';
|
|
21
|
-
import { asMock } from './test-utils';
|
|
22
|
-
|
|
23
|
-
jest.mock('prompts', () => jest.fn());
|
|
24
|
-
|
|
25
|
-
jest.mock('fs/promises', () => ({
|
|
26
|
-
readdir: jest.fn(),
|
|
27
|
-
}));
|
|
28
|
-
|
|
29
|
-
const config = { ...migrationConfigDefaults, basePath: __dirname };
|
|
30
|
-
|
|
31
|
-
describe('common', () => {
|
|
32
|
-
describe('processRakeDbConfig', () => {
|
|
33
|
-
it('should return config with defaults', () => {
|
|
34
|
-
const result = processRakeDbConfig({
|
|
35
|
-
basePath: __dirname,
|
|
36
|
-
migrationsPath: 'custom-path',
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
expect(result).toEqual({
|
|
40
|
-
basePath: __dirname,
|
|
41
|
-
migrationsPath: path.resolve(__dirname, 'custom-path'),
|
|
42
|
-
migrationsTable: 'schemaMigrations',
|
|
43
|
-
import: expect.any(Function),
|
|
44
|
-
log: true,
|
|
45
|
-
logger: console,
|
|
46
|
-
useCodeUpdater: true,
|
|
47
|
-
commands: {},
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
describe('getDatabaseAndUserFromOptions', () => {
|
|
53
|
-
it('should return data from databaseURL', () => {
|
|
54
|
-
const result = getDatabaseAndUserFromOptions({
|
|
55
|
-
databaseURL: 'postgres://user:password@localhost:5432/dbname',
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
expect(result).toEqual({
|
|
59
|
-
database: 'dbname',
|
|
60
|
-
user: 'user',
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should return data from options when no databaseURL', () => {
|
|
65
|
-
const result = getDatabaseAndUserFromOptions({
|
|
66
|
-
database: 'dbname',
|
|
67
|
-
user: 'user',
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
expect(result).toEqual({
|
|
71
|
-
database: 'dbname',
|
|
72
|
-
user: 'user',
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
describe('setAdapterOptions', () => {
|
|
78
|
-
it('should set options in databaseURL to postgres', () => {
|
|
79
|
-
const result = setAdapterOptions(
|
|
80
|
-
{
|
|
81
|
-
databaseURL: 'postgres://user:password@localhost:5432/dbname',
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
database: 'updated-db',
|
|
85
|
-
user: 'updated-user',
|
|
86
|
-
password: 'updated-password',
|
|
87
|
-
},
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
expect(result).toEqual({
|
|
91
|
-
databaseURL:
|
|
92
|
-
'postgres://updated-user:updated-password@localhost:5432/updated-db',
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should set object options', () => {
|
|
97
|
-
const result = setAdapterOptions(
|
|
98
|
-
{
|
|
99
|
-
database: 'dbname',
|
|
100
|
-
user: 'user',
|
|
101
|
-
password: 'password',
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
database: 'updated-db',
|
|
105
|
-
user: 'updated-user',
|
|
106
|
-
password: 'updated-password',
|
|
107
|
-
},
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
expect(result).toEqual({
|
|
111
|
-
database: 'updated-db',
|
|
112
|
-
user: 'updated-user',
|
|
113
|
-
password: 'updated-password',
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
describe('setAdminCredentialsToOptions', () => {
|
|
119
|
-
beforeEach(() => {
|
|
120
|
-
asMock(prompts).mockResolvedValueOnce({
|
|
121
|
-
confirm: true,
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
asMock(prompts).mockResolvedValueOnce({
|
|
125
|
-
user: 'admin-user',
|
|
126
|
-
password: 'admin-password',
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should set admin credentials to databaseURL', async () => {
|
|
131
|
-
const result = await setAdminCredentialsToOptions({
|
|
132
|
-
databaseURL: 'postgres://user:password@localhost:5432/dbname',
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
expect(result).toEqual({
|
|
136
|
-
databaseURL:
|
|
137
|
-
'postgres://admin-user:admin-password@localhost:5432/dbname',
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('should set admin credentials to options', async () => {
|
|
142
|
-
const result = await setAdminCredentialsToOptions({
|
|
143
|
-
database: 'dbname',
|
|
144
|
-
user: 'user',
|
|
145
|
-
password: 'password',
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
expect(result).toEqual({
|
|
149
|
-
database: 'dbname',
|
|
150
|
-
user: 'admin-user',
|
|
151
|
-
password: 'admin-password',
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
describe('createSchemaMigrations', () => {
|
|
157
|
-
const log = console.log;
|
|
158
|
-
const mockedLog = jest.fn();
|
|
159
|
-
const mockedQuery = jest.fn();
|
|
160
|
-
|
|
161
|
-
const db = new Adapter({
|
|
162
|
-
databaseURL: 'postgres://user:password@host:1234/db-name',
|
|
163
|
-
});
|
|
164
|
-
db.query = mockedQuery;
|
|
165
|
-
|
|
166
|
-
beforeAll(() => {
|
|
167
|
-
console.log = mockedLog;
|
|
168
|
-
});
|
|
169
|
-
beforeEach(() => {
|
|
170
|
-
jest.clearAllMocks();
|
|
171
|
-
});
|
|
172
|
-
afterAll(() => {
|
|
173
|
-
console.log = log;
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('should create a "schemaMigrations" table', async () => {
|
|
177
|
-
mockedQuery.mockReturnValueOnce(undefined);
|
|
178
|
-
|
|
179
|
-
await createSchemaMigrations(db, config);
|
|
180
|
-
|
|
181
|
-
expect(mockedQuery.mock.calls).toEqual([
|
|
182
|
-
[`CREATE TABLE "schemaMigrations" ( version TEXT NOT NULL )`],
|
|
183
|
-
]);
|
|
184
|
-
|
|
185
|
-
expect(mockedLog.mock.calls).toEqual([['Created versions table']]);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should inform if table already exists', async () => {
|
|
189
|
-
mockedQuery.mockRejectedValue({ code: '42P07' });
|
|
190
|
-
|
|
191
|
-
await createSchemaMigrations(db, config);
|
|
192
|
-
|
|
193
|
-
expect(mockedQuery.mock.calls).toEqual([
|
|
194
|
-
[`CREATE TABLE "schemaMigrations" ( version TEXT NOT NULL )`],
|
|
195
|
-
]);
|
|
196
|
-
|
|
197
|
-
expect(mockedLog.mock.calls).toEqual([['Versions table exists']]);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
describe('getFirstWordAndRest', () => {
|
|
202
|
-
it('should return pair of first word and rest', () => {
|
|
203
|
-
expect(getFirstWordAndRest('fooBarBaz')).toEqual(['foo', 'barBaz']);
|
|
204
|
-
expect(getFirstWordAndRest('foo-barBaz')).toEqual(['foo', 'barBaz']);
|
|
205
|
-
expect(getFirstWordAndRest('foo_barBaz')).toEqual(['foo', 'barBaz']);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('should return input when it is a single word', () => {
|
|
209
|
-
expect(getFirstWordAndRest('foo')).toEqual(['foo']);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
describe('getTextAfterTo', () => {
|
|
214
|
-
it('should return text after To or to', () => {
|
|
215
|
-
expect(getTextAfterTo('addColumnToTable')).toBe('table');
|
|
216
|
-
expect(getTextAfterTo('add-column-to-table')).toBe('table');
|
|
217
|
-
expect(getTextAfterTo('add_column_to_table')).toBe('table');
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
describe('getMigrationFiles', () => {
|
|
222
|
-
it('should return files with versions', async () => {
|
|
223
|
-
const version = '12345678901234';
|
|
224
|
-
const files = [`${version}_a.ts`, `${version}_b.ts`, `${version}_c.ts`];
|
|
225
|
-
(readdir as jest.Mock).mockReturnValueOnce(files);
|
|
226
|
-
|
|
227
|
-
const result = await getMigrationFiles(config, true);
|
|
228
|
-
expect(result).toEqual(
|
|
229
|
-
files.map((file) => ({
|
|
230
|
-
path: path.resolve(config.migrationsPath, file),
|
|
231
|
-
version,
|
|
232
|
-
})),
|
|
233
|
-
);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('should return empty array on error', async () => {
|
|
237
|
-
(readdir as jest.Mock).mockRejectedValue(new Error());
|
|
238
|
-
|
|
239
|
-
const result = await getMigrationFiles(config, true);
|
|
240
|
-
expect(result).toEqual([]);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it('should throw if file is not a .ts file', async () => {
|
|
244
|
-
(readdir as jest.Mock).mockReturnValueOnce(['file.js']);
|
|
245
|
-
|
|
246
|
-
await expect(getMigrationFiles(config, true)).rejects.toThrow(
|
|
247
|
-
'Only .ts files are supported',
|
|
248
|
-
);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it('should throw on improper version', async () => {
|
|
252
|
-
(readdir as jest.Mock).mockReturnValueOnce(['1234567890_file.ts']);
|
|
253
|
-
|
|
254
|
-
await expect(getMigrationFiles(config, true)).rejects.toThrow(
|
|
255
|
-
'Migration file name should start with 14 digit version',
|
|
256
|
-
);
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
describe('sortAsc', () => {
|
|
261
|
-
it('should sort ascending', () => {
|
|
262
|
-
expect(sortAsc(['a', 'c', 'b'])).toEqual(['a', 'b', 'c']);
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
describe('sortDesc', () => {
|
|
267
|
-
it('should sort descending', () => {
|
|
268
|
-
expect(sortDesc(['a', 'c', 'b'])).toEqual(['c', 'b', 'a']);
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
describe('joinWords', () => {
|
|
273
|
-
it('should join words', () => {
|
|
274
|
-
expect(joinWords('foo', 'bar', 'baz')).toEqual('fooBarBaz');
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
describe('joinColumns', () => {
|
|
279
|
-
it('should join columns', () => {
|
|
280
|
-
expect(joinColumns(['a', 'b', 'c'])).toBe('"a", "b", "c"');
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
describe('quoteWithSchema', () => {
|
|
285
|
-
it('should quote a name', () => {
|
|
286
|
-
expect(quoteWithSchema({ name: 'table' })).toBe('"table"');
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
it('should quote a name with schema', () => {
|
|
290
|
-
expect(quoteWithSchema({ schema: 'schema', name: 'table' })).toBe(
|
|
291
|
-
'"schema"."table"',
|
|
292
|
-
);
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
});
|
package/src/common.ts
DELETED
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Adapter,
|
|
3
|
-
AdapterOptions,
|
|
4
|
-
DbResult,
|
|
5
|
-
DefaultColumnTypes,
|
|
6
|
-
EnumColumn,
|
|
7
|
-
NoPrimaryKeyOption,
|
|
8
|
-
QueryLogOptions,
|
|
9
|
-
singleQuote,
|
|
10
|
-
} from 'pqb';
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import { readdir } from 'fs/promises';
|
|
13
|
-
import { RakeDbAst } from './ast';
|
|
14
|
-
import prompts from 'prompts';
|
|
15
|
-
import { TableQuery } from './migration/createTable';
|
|
16
|
-
|
|
17
|
-
type Db = DbResult<DefaultColumnTypes>;
|
|
18
|
-
|
|
19
|
-
export type RakeDbConfig = {
|
|
20
|
-
basePath: string;
|
|
21
|
-
migrationsPath: string;
|
|
22
|
-
migrationsTable: string;
|
|
23
|
-
commands: Record<
|
|
24
|
-
string,
|
|
25
|
-
(
|
|
26
|
-
options: AdapterOptions[],
|
|
27
|
-
config: RakeDbConfig,
|
|
28
|
-
args: string[],
|
|
29
|
-
) => Promise<void>
|
|
30
|
-
>;
|
|
31
|
-
import(path: string): Promise<void>;
|
|
32
|
-
noPrimaryKey?: NoPrimaryKeyOption;
|
|
33
|
-
appCodeUpdater?: AppCodeUpdater;
|
|
34
|
-
useCodeUpdater?: boolean;
|
|
35
|
-
beforeMigrate?(db: Db): Promise<void>;
|
|
36
|
-
afterMigrate?(db: Db): Promise<void>;
|
|
37
|
-
beforeRollback?(db: Db): Promise<void>;
|
|
38
|
-
afterRollback?(db: Db): Promise<void>;
|
|
39
|
-
} & QueryLogOptions;
|
|
40
|
-
|
|
41
|
-
export type AppCodeUpdater = (params: {
|
|
42
|
-
ast: RakeDbAst;
|
|
43
|
-
options: AdapterOptions;
|
|
44
|
-
basePath: string;
|
|
45
|
-
cache: object;
|
|
46
|
-
}) => Promise<void>;
|
|
47
|
-
|
|
48
|
-
export const migrationConfigDefaults: Omit<RakeDbConfig, 'basePath'> = {
|
|
49
|
-
migrationsPath: path.join('src', 'db', 'migrations'),
|
|
50
|
-
migrationsTable: 'schemaMigrations',
|
|
51
|
-
commands: {},
|
|
52
|
-
import: (path: string) => import(path),
|
|
53
|
-
log: true,
|
|
54
|
-
logger: console,
|
|
55
|
-
useCodeUpdater: true,
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export const processRakeDbConfig = (
|
|
59
|
-
config: Partial<RakeDbConfig>,
|
|
60
|
-
): RakeDbConfig => {
|
|
61
|
-
const result = { ...migrationConfigDefaults, ...config };
|
|
62
|
-
|
|
63
|
-
if (!result.basePath) {
|
|
64
|
-
let stack: NodeJS.CallSite[] | undefined;
|
|
65
|
-
Error.prepareStackTrace = (_, s) => (stack = s);
|
|
66
|
-
new Error().stack;
|
|
67
|
-
if (stack) {
|
|
68
|
-
const thisFile = stack[0]?.getFileName();
|
|
69
|
-
const thisDir = thisFile && path.dirname(thisFile);
|
|
70
|
-
for (const item of stack) {
|
|
71
|
-
let file = item.getFileName();
|
|
72
|
-
if (
|
|
73
|
-
!file ||
|
|
74
|
-
path.dirname(file) === thisDir ||
|
|
75
|
-
/\bnode_modules\b/.test(file)
|
|
76
|
-
) {
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// on Windows with ESM file is file:///C:/path/to/file.ts
|
|
81
|
-
// it is not a valid URL
|
|
82
|
-
if (/file:\/\/\/\w+:\//.test(file)) {
|
|
83
|
-
file = decodeURI(file.slice(8));
|
|
84
|
-
} else {
|
|
85
|
-
try {
|
|
86
|
-
file = new URL(file).pathname;
|
|
87
|
-
} catch (_) {}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
result.basePath = path.dirname(file);
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (!result.basePath) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
'Failed to determine path to db script. Please set basePath option of rakeDb',
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (!path.isAbsolute(result.migrationsPath)) {
|
|
103
|
-
result.migrationsPath = path.resolve(
|
|
104
|
-
result.basePath,
|
|
105
|
-
result.migrationsPath,
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return result as RakeDbConfig;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
export const getDatabaseAndUserFromOptions = (
|
|
113
|
-
options: AdapterOptions,
|
|
114
|
-
): { database: string; user: string } => {
|
|
115
|
-
if (options.databaseURL) {
|
|
116
|
-
const url = new URL(options.databaseURL);
|
|
117
|
-
return {
|
|
118
|
-
database: url.pathname.slice(1),
|
|
119
|
-
user: url.username,
|
|
120
|
-
};
|
|
121
|
-
} else {
|
|
122
|
-
return {
|
|
123
|
-
database: options.database as string,
|
|
124
|
-
user: options.user as string,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
export const setAdapterOptions = (
|
|
130
|
-
options: AdapterOptions,
|
|
131
|
-
set: { database?: string; user?: string; password?: string },
|
|
132
|
-
): AdapterOptions => {
|
|
133
|
-
if (options.databaseURL) {
|
|
134
|
-
const url = new URL(options.databaseURL);
|
|
135
|
-
|
|
136
|
-
if ('database' in set) {
|
|
137
|
-
url.pathname = `/${set.database}`;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (set.user !== undefined) {
|
|
141
|
-
url.username = set.user;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (set.password !== undefined) {
|
|
145
|
-
url.password = set.password;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return { ...options, databaseURL: url.toString() };
|
|
149
|
-
} else {
|
|
150
|
-
return {
|
|
151
|
-
...options,
|
|
152
|
-
...set,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
export const setAdminCredentialsToOptions = async (
|
|
158
|
-
options: AdapterOptions,
|
|
159
|
-
create?: boolean,
|
|
160
|
-
): Promise<AdapterOptions | undefined> => {
|
|
161
|
-
const confirm = await prompts([
|
|
162
|
-
{
|
|
163
|
-
message: `Would you like to share admin credentials to ${
|
|
164
|
-
create ? 'create' : 'drop'
|
|
165
|
-
} a database`,
|
|
166
|
-
type: 'confirm',
|
|
167
|
-
name: 'confirm',
|
|
168
|
-
initial: true,
|
|
169
|
-
},
|
|
170
|
-
]);
|
|
171
|
-
|
|
172
|
-
if (!confirm.confirm) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const values = await prompts([
|
|
177
|
-
{
|
|
178
|
-
message: 'Enter admin user:',
|
|
179
|
-
type: 'text',
|
|
180
|
-
name: 'user',
|
|
181
|
-
initial: 'postgres',
|
|
182
|
-
min: 1,
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
message: 'Enter admin password:',
|
|
186
|
-
type: 'password',
|
|
187
|
-
name: 'password',
|
|
188
|
-
},
|
|
189
|
-
]);
|
|
190
|
-
|
|
191
|
-
return setAdapterOptions(options, {
|
|
192
|
-
...values,
|
|
193
|
-
password: values.password || undefined,
|
|
194
|
-
});
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
export const createSchemaMigrations = async (
|
|
198
|
-
db: Adapter,
|
|
199
|
-
config: Pick<RakeDbConfig, 'migrationsTable'>,
|
|
200
|
-
) => {
|
|
201
|
-
try {
|
|
202
|
-
await db.query(
|
|
203
|
-
`CREATE TABLE ${quoteWithSchema({
|
|
204
|
-
name: config.migrationsTable,
|
|
205
|
-
})} ( version TEXT NOT NULL )`,
|
|
206
|
-
);
|
|
207
|
-
console.log('Created versions table');
|
|
208
|
-
} catch (err) {
|
|
209
|
-
if ((err as Record<string, unknown>).code === '42P07') {
|
|
210
|
-
console.log('Versions table exists');
|
|
211
|
-
} else {
|
|
212
|
-
throw err;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
export const getFirstWordAndRest = (
|
|
218
|
-
input: string,
|
|
219
|
-
): [string] | [string, string] => {
|
|
220
|
-
const index = input.search(/(?=[A-Z])|[-_]/);
|
|
221
|
-
if (index !== -1) {
|
|
222
|
-
const restStart =
|
|
223
|
-
input[index] === '-' || input[index] === '_' ? index + 1 : index;
|
|
224
|
-
const rest = input.slice(restStart);
|
|
225
|
-
return [input.slice(0, index), rest[0].toLowerCase() + rest.slice(1)];
|
|
226
|
-
} else {
|
|
227
|
-
return [input];
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
const getTextAfterRegExp = (
|
|
232
|
-
input: string,
|
|
233
|
-
regex: RegExp,
|
|
234
|
-
length: number,
|
|
235
|
-
): string | undefined => {
|
|
236
|
-
let index = input.search(regex);
|
|
237
|
-
if (index === -1) return;
|
|
238
|
-
|
|
239
|
-
if (input[index] === '-' || input[index] === '_') index++;
|
|
240
|
-
index += length;
|
|
241
|
-
|
|
242
|
-
const start = input[index] == '-' || input[index] === '_' ? index + 1 : index;
|
|
243
|
-
const text = input.slice(start);
|
|
244
|
-
return text[0].toLowerCase() + text.slice(1);
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
export const getTextAfterTo = (input: string): string | undefined => {
|
|
248
|
-
return getTextAfterRegExp(input, /(To|-to|_to)[A-Z-_]/, 2);
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
export const getTextAfterFrom = (input: string): string | undefined => {
|
|
252
|
-
return getTextAfterRegExp(input, /(From|-from|_from)[A-Z-_]/, 4);
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
export type MigrationFile = {
|
|
256
|
-
path: string;
|
|
257
|
-
version: string;
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
export const getMigrationFiles = async (
|
|
261
|
-
config: RakeDbConfig,
|
|
262
|
-
up: boolean,
|
|
263
|
-
): Promise<MigrationFile[]> => {
|
|
264
|
-
const { migrationsPath } = config;
|
|
265
|
-
|
|
266
|
-
let files: string[];
|
|
267
|
-
try {
|
|
268
|
-
files = await readdir(migrationsPath);
|
|
269
|
-
} catch (_) {
|
|
270
|
-
return [];
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const sort = up ? sortAsc : sortDesc;
|
|
274
|
-
return sort(files).map((file) => {
|
|
275
|
-
if (!file.endsWith('.ts')) {
|
|
276
|
-
throw new Error(
|
|
277
|
-
`Only .ts files are supported for migration, received: ${file}`,
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const timestampMatch = file.match(/^(\d{14})\D/);
|
|
282
|
-
if (!timestampMatch) {
|
|
283
|
-
throw new Error(
|
|
284
|
-
`Migration file name should start with 14 digit version, received ${file}`,
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return {
|
|
289
|
-
path: path.resolve(migrationsPath, file),
|
|
290
|
-
version: timestampMatch[1],
|
|
291
|
-
};
|
|
292
|
-
});
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
export const sortAsc = (arr: string[]) => arr.sort();
|
|
296
|
-
|
|
297
|
-
export const sortDesc = (arr: string[]) => arr.sort((a, b) => (a > b ? -1 : 1));
|
|
298
|
-
|
|
299
|
-
export const joinWords = (...words: string[]) => {
|
|
300
|
-
return words
|
|
301
|
-
.slice(1)
|
|
302
|
-
.reduce(
|
|
303
|
-
(acc, word) => acc + word[0].toUpperCase() + word.slice(1),
|
|
304
|
-
words[0],
|
|
305
|
-
);
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
export const joinColumns = (columns: string[]) => {
|
|
309
|
-
return columns.map((column) => `"${column}"`).join(', ');
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
export const quoteWithSchema = ({
|
|
313
|
-
schema,
|
|
314
|
-
name,
|
|
315
|
-
}: {
|
|
316
|
-
schema?: string;
|
|
317
|
-
name: string;
|
|
318
|
-
}) => {
|
|
319
|
-
return schema ? `"${schema}"."${name}"` : `"${name}"`;
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
export const getSchemaAndTableFromName = (
|
|
323
|
-
name: string,
|
|
324
|
-
): [string | undefined, string] => {
|
|
325
|
-
const index = name.indexOf('.');
|
|
326
|
-
return index !== -1
|
|
327
|
-
? [name.slice(0, index), name.slice(index + 1)]
|
|
328
|
-
: [undefined, name];
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
export const quoteSchemaTable = ({
|
|
332
|
-
schema,
|
|
333
|
-
name,
|
|
334
|
-
}: {
|
|
335
|
-
schema?: string;
|
|
336
|
-
name: string;
|
|
337
|
-
}) => {
|
|
338
|
-
return singleQuote(schema ? `${schema}.${name}` : name);
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
export const makePopulateEnumQuery = (item: EnumColumn): TableQuery => {
|
|
342
|
-
const [schema, name] = getSchemaAndTableFromName(item.enumName);
|
|
343
|
-
return {
|
|
344
|
-
text: `SELECT unnest(enum_range(NULL::${quoteWithSchema({
|
|
345
|
-
schema,
|
|
346
|
-
name,
|
|
347
|
-
})}))::text`,
|
|
348
|
-
then(result) {
|
|
349
|
-
// populate empty options array with values from db
|
|
350
|
-
item.options.push(...result.rows.map(([value]) => value));
|
|
351
|
-
},
|
|
352
|
-
};
|
|
353
|
-
};
|
package/src/errors.ts
DELETED
package/src/index.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export * from './commands/createOrDrop';
|
|
2
|
-
export * from './commands/generate';
|
|
3
|
-
export * from './commands/migrateOrRollback';
|
|
4
|
-
export { change } from './migration/change';
|
|
5
|
-
export * from './migration/migration';
|
|
6
|
-
export { rakeDb } from './rakeDb';
|
|
7
|
-
export * from './ast';
|
|
8
|
-
export type { RakeDbConfig, AppCodeUpdater } from './common';
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { change, clearChanges, getCurrentChanges } from './change';
|
|
2
|
-
|
|
3
|
-
describe('change', () => {
|
|
4
|
-
const fn = async () => {};
|
|
5
|
-
|
|
6
|
-
it('should push callback to currentChanges', () => {
|
|
7
|
-
change(fn);
|
|
8
|
-
expect(getCurrentChanges()).toEqual([fn]);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('should clear changes', () => {
|
|
12
|
-
getCurrentChanges().push(fn);
|
|
13
|
-
clearChanges();
|
|
14
|
-
expect(getCurrentChanges()).toEqual([]);
|
|
15
|
-
});
|
|
16
|
-
});
|
package/src/migration/change.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Migration } from './migration';
|
|
2
|
-
|
|
3
|
-
const currentChanges: ChangeCallback[] = [];
|
|
4
|
-
|
|
5
|
-
export type ChangeCallback = (db: Migration, up: boolean) => Promise<void>;
|
|
6
|
-
|
|
7
|
-
export const change = (fn: ChangeCallback) => {
|
|
8
|
-
currentChanges.push(fn);
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export const clearChanges = () => {
|
|
12
|
-
currentChanges.length = 0;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const getCurrentChanges = () => currentChanges;
|