@flusys/nestjs-core 7.0.2 → 8.0.0-beta.1
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/README.md +2 -2
- package/config/index.d.ts +1 -1
- package/constants/index.d.ts +1 -1
- package/docs/docs.config.d.ts +1 -1
- package/docs/index.d.ts +1 -1
- package/fesm/229.js +729 -0
- package/fesm/config/index.js +126 -1
- package/fesm/constants/index.js +29 -1
- package/fesm/docs/index.js +288 -1
- package/fesm/index.js +124 -5
- package/fesm/interfaces/index.js +18 -4
- package/fesm/migration/cli.js +19 -2
- package/fesm/migration/index.js +45 -3
- package/fesm/runtime.js +85 -0
- package/fesm/utils/index.js +131 -1
- package/index.d.ts +5 -5
- package/interfaces/app-config.interfaces.d.ts +2 -1
- package/interfaces/database.interface.d.ts +1 -0
- package/interfaces/index.d.ts +4 -4
- package/interfaces/migration.interface.d.ts +2 -2
- package/migration/cli.d.ts +0 -1
- package/migration/datasource.factory.d.ts +1 -1
- package/migration/index.d.ts +3 -3
- package/migration/migration.cli.d.ts +1 -1
- package/migration/migration.runner.d.ts +1 -1
- package/package.json +25 -33
- package/utils/datasource-config.builder.d.ts +2 -2
- package/utils/index.d.ts +1 -1
- package/cjs/config/env-config.service.js +0 -162
- package/cjs/config/env-config.service.spec.js +0 -325
- package/cjs/config/index.js +0 -18
- package/cjs/constants/database.constants.js +0 -20
- package/cjs/constants/index.js +0 -18
- package/cjs/docs/docs.config.js +0 -276
- package/cjs/docs/docs.config.spec.js +0 -923
- package/cjs/docs/index.js +0 -18
- package/cjs/index.js +0 -23
- package/cjs/interfaces/app-config.interfaces.js +0 -4
- package/cjs/interfaces/base-entity.interface.js +0 -9
- package/cjs/interfaces/database.interface.js +0 -4
- package/cjs/interfaces/index.js +0 -21
- package/cjs/interfaces/migration.interface.js +0 -4
- package/cjs/migration/cli.js +0 -10
- package/cjs/migration/datasource.factory.js +0 -43
- package/cjs/migration/datasource.factory.spec.js +0 -113
- package/cjs/migration/index.js +0 -45
- package/cjs/migration/migration.cli.js +0 -231
- package/cjs/migration/migration.cli.spec.js +0 -293
- package/cjs/migration/migration.runner.js +0 -276
- package/cjs/migration/migration.runner.spec.js +0 -268
- package/cjs/utils/datasource-config.builder.js +0 -103
- package/cjs/utils/datasource-config.builder.spec.js +0 -232
- package/cjs/utils/index.js +0 -18
- package/fesm/config/env-config.service.js +0 -112
- package/fesm/config/env-config.service.spec.js +0 -321
- package/fesm/constants/database.constants.js +0 -2
- package/fesm/docs/docs.config.js +0 -258
- package/fesm/docs/docs.config.spec.js +0 -919
- package/fesm/interfaces/app-config.interfaces.js +0 -1
- package/fesm/interfaces/base-entity.interface.js +0 -6
- package/fesm/interfaces/database.interface.js +0 -1
- package/fesm/interfaces/migration.interface.js +0 -1
- package/fesm/migration/datasource.factory.js +0 -25
- package/fesm/migration/datasource.factory.spec.js +0 -109
- package/fesm/migration/migration.cli.js +0 -180
- package/fesm/migration/migration.cli.spec.js +0 -289
- package/fesm/migration/migration.runner.js +0 -202
- package/fesm/migration/migration.runner.spec.js +0 -264
- package/fesm/utils/datasource-config.builder.js +0 -84
- package/fesm/utils/datasource-config.builder.spec.js +0 -228
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
const _migrationrunner = require("./migration.runner");
|
|
6
|
-
const _datasourcefactory = require("./datasource.factory");
|
|
7
|
-
const mockExistsSync = jest.fn();
|
|
8
|
-
const mockMkdirSync = jest.fn();
|
|
9
|
-
const mockReaddirSync = jest.fn();
|
|
10
|
-
const mockRmSync = jest.fn();
|
|
11
|
-
const mockExecSync = jest.fn();
|
|
12
|
-
jest.mock('fs', ()=>({
|
|
13
|
-
existsSync: (...args)=>mockExistsSync(...args),
|
|
14
|
-
mkdirSync: (...args)=>mockMkdirSync(...args),
|
|
15
|
-
readdirSync: (...args)=>mockReaddirSync(...args),
|
|
16
|
-
rmSync: (...args)=>mockRmSync(...args)
|
|
17
|
-
}));
|
|
18
|
-
jest.mock('child_process', ()=>({
|
|
19
|
-
execSync: (...args)=>mockExecSync(...args)
|
|
20
|
-
}));
|
|
21
|
-
jest.mock('../config', ()=>({
|
|
22
|
-
envConfig: {
|
|
23
|
-
getEnv: jest.fn().mockReturnValue({
|
|
24
|
-
NODE_ENV: 'test'
|
|
25
|
-
})
|
|
26
|
-
}
|
|
27
|
-
}));
|
|
28
|
-
const mockRunMigrations = jest.fn();
|
|
29
|
-
const mockUndoLastMigration = jest.fn();
|
|
30
|
-
const mockShowMigrations = jest.fn();
|
|
31
|
-
const mockDestroy = jest.fn();
|
|
32
|
-
const mockDsInitialize = jest.fn();
|
|
33
|
-
const mockAdminInitialize = jest.fn();
|
|
34
|
-
const mockAdminQuery = jest.fn();
|
|
35
|
-
const mockAdminDestroy = jest.fn();
|
|
36
|
-
jest.mock('./datasource.factory', ()=>({
|
|
37
|
-
initializeDataSource: jest.fn()
|
|
38
|
-
}));
|
|
39
|
-
jest.mock('typeorm', ()=>({
|
|
40
|
-
DataSource: jest.fn().mockImplementation(()=>({
|
|
41
|
-
initialize: mockAdminInitialize,
|
|
42
|
-
query: mockAdminQuery,
|
|
43
|
-
destroy: mockAdminDestroy
|
|
44
|
-
}))
|
|
45
|
-
}));
|
|
46
|
-
describe('migration.runner', ()=>{
|
|
47
|
-
const baseConfig = {
|
|
48
|
-
defaultDatabaseConfig: {
|
|
49
|
-
type: 'postgres',
|
|
50
|
-
database: 'flusys_default'
|
|
51
|
-
},
|
|
52
|
-
migrationsPath: 'migrations',
|
|
53
|
-
entities: []
|
|
54
|
-
};
|
|
55
|
-
let logSpy;
|
|
56
|
-
let errorSpy;
|
|
57
|
-
beforeEach(()=>{
|
|
58
|
-
jest.clearAllMocks();
|
|
59
|
-
logSpy = jest.spyOn(console, 'log').mockImplementation(()=>undefined);
|
|
60
|
-
errorSpy = jest.spyOn(console, 'error').mockImplementation(()=>undefined);
|
|
61
|
-
_datasourcefactory.initializeDataSource.mockResolvedValue({
|
|
62
|
-
runMigrations: mockRunMigrations,
|
|
63
|
-
undoLastMigration: mockUndoLastMigration,
|
|
64
|
-
showMigrations: mockShowMigrations,
|
|
65
|
-
destroy: mockDestroy,
|
|
66
|
-
initialize: mockDsInitialize
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
afterEach(()=>{
|
|
70
|
-
logSpy.mockRestore();
|
|
71
|
-
errorSpy.mockRestore();
|
|
72
|
-
});
|
|
73
|
-
describe('ensureMigrationsFolder', ()=>{
|
|
74
|
-
it('should create the folder when it does not exist', ()=>{
|
|
75
|
-
mockExistsSync.mockReturnValue(false);
|
|
76
|
-
const folder = (0, _migrationrunner.ensureMigrationsFolder)('migrations', 'tenant-a');
|
|
77
|
-
expect(folder).toBe('migrations/tenant-a');
|
|
78
|
-
expect(mockMkdirSync).toHaveBeenCalledWith('migrations/tenant-a', {
|
|
79
|
-
recursive: true
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
it('should not create the folder when it already exists', ()=>{
|
|
83
|
-
mockExistsSync.mockReturnValue(true);
|
|
84
|
-
(0, _migrationrunner.ensureMigrationsFolder)('migrations');
|
|
85
|
-
expect(mockMkdirSync).not.toHaveBeenCalled();
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
describe('generateMigration', ()=>{
|
|
89
|
-
it('should shell out to the typeorm CLI with the resolved datasource path', async ()=>{
|
|
90
|
-
mockExistsSync.mockImplementation((p)=>p.includes('flusysnest'));
|
|
91
|
-
await (0, _migrationrunner.generateMigration)(baseConfig, 'AddProductTable', 'tenant-a');
|
|
92
|
-
expect(mockExecSync).toHaveBeenCalledTimes(1);
|
|
93
|
-
const [command, options] = mockExecSync.mock.calls[0];
|
|
94
|
-
expect(command).toContain('migration:generate');
|
|
95
|
-
expect(command).toContain('migrations/tenant-a/AddProductTable');
|
|
96
|
-
expect(command).toContain('projects/flusysnest/src/persistence/migration.config.ts');
|
|
97
|
-
expect(options.env.TENANT_ID).toBe('tenant-a');
|
|
98
|
-
});
|
|
99
|
-
it('should use an explicit datasource path when provided', async ()=>{
|
|
100
|
-
mockExistsSync.mockReturnValue(false);
|
|
101
|
-
await (0, _migrationrunner.generateMigration)(baseConfig, 'AddProductTable', undefined, 'custom/datasource.ts');
|
|
102
|
-
const [command] = mockExecSync.mock.calls[0];
|
|
103
|
-
expect(command).toContain('-d "custom/datasource.ts"');
|
|
104
|
-
});
|
|
105
|
-
it('should throw when no datasource file can be found and none is specified', async ()=>{
|
|
106
|
-
mockExistsSync.mockReturnValue(false);
|
|
107
|
-
await expect((0, _migrationrunner.generateMigration)(baseConfig, 'AddProductTable')).rejects.toThrow('Could not find datasource file. Specify with --datasource=<path>');
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
describe('runMigrations', ()=>{
|
|
111
|
-
it('should report success with the list of executed migrations', async ()=>{
|
|
112
|
-
mockRunMigrations.mockResolvedValue([
|
|
113
|
-
{
|
|
114
|
-
name: 'M1'
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
name: 'M2'
|
|
118
|
-
}
|
|
119
|
-
]);
|
|
120
|
-
const result = await (0, _migrationrunner.runMigrations)(baseConfig);
|
|
121
|
-
expect(result).toEqual({
|
|
122
|
-
tenantId: undefined,
|
|
123
|
-
database: 'flusys_default',
|
|
124
|
-
success: true,
|
|
125
|
-
migrationsRun: [
|
|
126
|
-
'M1',
|
|
127
|
-
'M2'
|
|
128
|
-
]
|
|
129
|
-
});
|
|
130
|
-
expect(mockDestroy).toHaveBeenCalled();
|
|
131
|
-
});
|
|
132
|
-
it('should report no pending migrations when none run', async ()=>{
|
|
133
|
-
mockRunMigrations.mockResolvedValue([]);
|
|
134
|
-
const result = await (0, _migrationrunner.runMigrations)(baseConfig);
|
|
135
|
-
expect(result.migrationsRun).toEqual([]);
|
|
136
|
-
expect(result.success).toBe(true);
|
|
137
|
-
});
|
|
138
|
-
it('should capture errors and mark the result as failed', async ()=>{
|
|
139
|
-
_datasourcefactory.initializeDataSource.mockRejectedValue(new Error('connection refused'));
|
|
140
|
-
const result = await (0, _migrationrunner.runMigrations)(baseConfig, 'tenant-a');
|
|
141
|
-
expect(result).toEqual({
|
|
142
|
-
tenantId: 'tenant-a',
|
|
143
|
-
database: 'flusys_default',
|
|
144
|
-
success: false,
|
|
145
|
-
error: 'connection refused'
|
|
146
|
-
});
|
|
147
|
-
expect(errorSpy).toHaveBeenCalled();
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
describe('revertMigration', ()=>{
|
|
151
|
-
it('should undo the last migration and close the connection', async ()=>{
|
|
152
|
-
const result = await (0, _migrationrunner.revertMigration)(baseConfig);
|
|
153
|
-
expect(mockUndoLastMigration).toHaveBeenCalled();
|
|
154
|
-
expect(mockDestroy).toHaveBeenCalled();
|
|
155
|
-
expect(result.success).toBe(true);
|
|
156
|
-
});
|
|
157
|
-
it('should capture revert errors', async ()=>{
|
|
158
|
-
mockUndoLastMigration.mockRejectedValue(new Error('no migrations to revert'));
|
|
159
|
-
const result = await (0, _migrationrunner.revertMigration)(baseConfig);
|
|
160
|
-
expect(result.success).toBe(false);
|
|
161
|
-
expect(result.error).toBe('no migrations to revert');
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
describe('migrationStatus', ()=>{
|
|
165
|
-
it('should report pending status', async ()=>{
|
|
166
|
-
mockShowMigrations.mockResolvedValue(true);
|
|
167
|
-
const result = await (0, _migrationrunner.migrationStatus)(baseConfig);
|
|
168
|
-
expect(result.migrationsRun).toEqual([
|
|
169
|
-
'Has pending'
|
|
170
|
-
]);
|
|
171
|
-
});
|
|
172
|
-
it('should report up-to-date status', async ()=>{
|
|
173
|
-
mockShowMigrations.mockResolvedValue(false);
|
|
174
|
-
const result = await (0, _migrationrunner.migrationStatus)(baseConfig);
|
|
175
|
-
expect(result.migrationsRun).toEqual([
|
|
176
|
-
'Up to date'
|
|
177
|
-
]);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
describe('resetDatabase', ()=>{
|
|
181
|
-
it('should remove migration files then drop/recreate a postgres database', async ()=>{
|
|
182
|
-
mockExistsSync.mockReturnValue(true);
|
|
183
|
-
mockReaddirSync.mockReturnValue([
|
|
184
|
-
'1-init.ts',
|
|
185
|
-
'2-add.js',
|
|
186
|
-
'readme.md'
|
|
187
|
-
]);
|
|
188
|
-
mockAdminQuery.mockResolvedValue(undefined);
|
|
189
|
-
const config = {
|
|
190
|
-
defaultDatabaseConfig: {
|
|
191
|
-
type: 'postgres',
|
|
192
|
-
host: 'localhost',
|
|
193
|
-
port: 5432,
|
|
194
|
-
username: 'root',
|
|
195
|
-
password: 'pw',
|
|
196
|
-
database: 'flusys_default'
|
|
197
|
-
},
|
|
198
|
-
migrationsPath: 'migrations',
|
|
199
|
-
entities: []
|
|
200
|
-
};
|
|
201
|
-
const result = await (0, _migrationrunner.resetDatabase)(config);
|
|
202
|
-
expect(mockRmSync).toHaveBeenCalledTimes(2);
|
|
203
|
-
expect(mockAdminQuery).toHaveBeenCalledWith(expect.stringContaining('DROP DATABASE IF EXISTS "flusys_default"'));
|
|
204
|
-
expect(mockAdminDestroy).toHaveBeenCalled();
|
|
205
|
-
expect(result.success).toBe(true);
|
|
206
|
-
});
|
|
207
|
-
it('should delete the sqlite file directly for sqlite databases', async ()=>{
|
|
208
|
-
mockExistsSync.mockReturnValue(true);
|
|
209
|
-
mockReaddirSync.mockReturnValue([]);
|
|
210
|
-
const config = {
|
|
211
|
-
defaultDatabaseConfig: {
|
|
212
|
-
type: 'sqlite',
|
|
213
|
-
database: 'flusys.sqlite'
|
|
214
|
-
},
|
|
215
|
-
migrationsPath: 'migrations',
|
|
216
|
-
entities: []
|
|
217
|
-
};
|
|
218
|
-
const result = await (0, _migrationrunner.resetDatabase)(config);
|
|
219
|
-
expect(mockRmSync).toHaveBeenCalledWith('flusys.sqlite');
|
|
220
|
-
expect(mockAdminInitialize).not.toHaveBeenCalled();
|
|
221
|
-
expect(result.success).toBe(true);
|
|
222
|
-
});
|
|
223
|
-
it('should fail for unsupported database types', async ()=>{
|
|
224
|
-
mockExistsSync.mockReturnValue(false);
|
|
225
|
-
const config = {
|
|
226
|
-
defaultDatabaseConfig: {
|
|
227
|
-
type: 'mssql',
|
|
228
|
-
database: 'flusys'
|
|
229
|
-
},
|
|
230
|
-
migrationsPath: 'migrations',
|
|
231
|
-
entities: []
|
|
232
|
-
};
|
|
233
|
-
const result = await (0, _migrationrunner.resetDatabase)(config);
|
|
234
|
-
expect(result.success).toBe(false);
|
|
235
|
-
expect(result.error).toContain("Database reset is not supported for type 'mssql'");
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
describe('runForAllTenants', ()=>{
|
|
239
|
-
it('should run the given command across all configured tenants and summarize results', async ()=>{
|
|
240
|
-
mockRunMigrations.mockResolvedValue([
|
|
241
|
-
{
|
|
242
|
-
name: 'M1'
|
|
243
|
-
}
|
|
244
|
-
]);
|
|
245
|
-
const config = {
|
|
246
|
-
...baseConfig,
|
|
247
|
-
tenants: [
|
|
248
|
-
{
|
|
249
|
-
id: 'tenant-a',
|
|
250
|
-
database: 'db_a'
|
|
251
|
-
},
|
|
252
|
-
{
|
|
253
|
-
id: 'tenant-b',
|
|
254
|
-
database: 'db_b'
|
|
255
|
-
}
|
|
256
|
-
]
|
|
257
|
-
};
|
|
258
|
-
const results = await (0, _migrationrunner.runForAllTenants)(config, 'run');
|
|
259
|
-
expect(results).toHaveLength(2);
|
|
260
|
-
expect(results.every((r)=>r.success)).toBe(true);
|
|
261
|
-
expect(_datasourcefactory.initializeDataSource).toHaveBeenCalledTimes(2);
|
|
262
|
-
});
|
|
263
|
-
it('should return an empty result set when no tenants are configured', async ()=>{
|
|
264
|
-
const results = await (0, _migrationrunner.runForAllTenants)(baseConfig, 'status');
|
|
265
|
-
expect(results).toEqual([]);
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
});
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
get buildDataSourceOptions () {
|
|
13
|
-
return buildDataSourceOptions;
|
|
14
|
-
},
|
|
15
|
-
get getActiveTenants () {
|
|
16
|
-
return getActiveTenants;
|
|
17
|
-
},
|
|
18
|
-
get getDatabaseForTenant () {
|
|
19
|
-
return getDatabaseForTenant;
|
|
20
|
-
},
|
|
21
|
-
get getMigrationsFolderPath () {
|
|
22
|
-
return getMigrationsFolderPath;
|
|
23
|
-
},
|
|
24
|
-
get getMigrationsGlobPattern () {
|
|
25
|
-
return getMigrationsGlobPattern;
|
|
26
|
-
},
|
|
27
|
-
get resolveDatabaseConnection () {
|
|
28
|
-
return resolveDatabaseConnection;
|
|
29
|
-
},
|
|
30
|
-
get resolveEntities () {
|
|
31
|
-
return resolveEntities;
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
function buildDataSourceOptions(config, entities, migrationsPath, migrationsTableName = 'migrations') {
|
|
35
|
-
return {
|
|
36
|
-
type: config.type,
|
|
37
|
-
host: config.host,
|
|
38
|
-
port: config.port,
|
|
39
|
-
username: config.username,
|
|
40
|
-
password: config.password,
|
|
41
|
-
database: config.database || '',
|
|
42
|
-
entities,
|
|
43
|
-
migrations: [
|
|
44
|
-
migrationsPath
|
|
45
|
-
],
|
|
46
|
-
migrationsTableName,
|
|
47
|
-
synchronize: false
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
function getMigrationsFolderPath(basePath, tenantId) {
|
|
51
|
-
const folder = tenantId || 'default';
|
|
52
|
-
return `${basePath}/${folder}`;
|
|
53
|
-
}
|
|
54
|
-
function getMigrationsGlobPattern(basePath, tenantId) {
|
|
55
|
-
return `${getMigrationsFolderPath(basePath, tenantId)}/*{.ts,.js}`;
|
|
56
|
-
}
|
|
57
|
-
function getDatabaseForTenant(config, tenantId) {
|
|
58
|
-
if (tenantId && config.tenants) {
|
|
59
|
-
const tenant = config.tenants.find((t)=>t.id === tenantId);
|
|
60
|
-
if (!tenant) {
|
|
61
|
-
throw new Error(`Tenant '${tenantId}' not found. Available: ${config.tenants.map((t)=>t.id).join(', ')}`);
|
|
62
|
-
}
|
|
63
|
-
return {
|
|
64
|
-
database: tenant.database,
|
|
65
|
-
tenant,
|
|
66
|
-
tenantConfig: tenant
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
// Default config - use bootstrapAppConfig for feature flags
|
|
70
|
-
const defaultTenantConfig = {
|
|
71
|
-
id: 'default',
|
|
72
|
-
database: config.defaultDatabaseConfig.database || 'default',
|
|
73
|
-
enableCompanyFeature: config.bootstrapAppConfig?.enableCompanyFeature ?? false,
|
|
74
|
-
permissionMode: config.bootstrapAppConfig?.permissionMode ?? 'FULL'
|
|
75
|
-
};
|
|
76
|
-
return {
|
|
77
|
-
database: defaultTenantConfig.database,
|
|
78
|
-
tenantConfig: defaultTenantConfig
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function resolveDatabaseConnection(config, tenantId) {
|
|
82
|
-
const { database, tenant } = getDatabaseForTenant(config, tenantId);
|
|
83
|
-
return tenant ? {
|
|
84
|
-
...config.defaultDatabaseConfig,
|
|
85
|
-
host: tenant.host ?? config.defaultDatabaseConfig.host,
|
|
86
|
-
port: tenant.port ?? config.defaultDatabaseConfig.port,
|
|
87
|
-
username: tenant.username ?? config.defaultDatabaseConfig.username,
|
|
88
|
-
password: tenant.password ?? config.defaultDatabaseConfig.password,
|
|
89
|
-
database: tenant.database
|
|
90
|
-
} : {
|
|
91
|
-
...config.defaultDatabaseConfig,
|
|
92
|
-
database
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function resolveEntities(config, tenantConfig) {
|
|
96
|
-
if (typeof config.entities === 'function') {
|
|
97
|
-
return config.entities(tenantConfig);
|
|
98
|
-
}
|
|
99
|
-
return config.entities;
|
|
100
|
-
}
|
|
101
|
-
function getActiveTenants(config) {
|
|
102
|
-
return config.tenants || [];
|
|
103
|
-
}
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
const _datasourceconfigbuilder = require("./datasource-config.builder");
|
|
6
|
-
describe('datasource-config.builder', ()=>{
|
|
7
|
-
describe('buildDataSourceOptions', ()=>{
|
|
8
|
-
it('should build options with synchronize disabled', ()=>{
|
|
9
|
-
const config = {
|
|
10
|
-
type: 'postgres',
|
|
11
|
-
host: 'localhost',
|
|
12
|
-
port: 5432,
|
|
13
|
-
username: 'root',
|
|
14
|
-
password: 'pw',
|
|
15
|
-
database: 'flusys'
|
|
16
|
-
};
|
|
17
|
-
const options = (0, _datasourceconfigbuilder.buildDataSourceOptions)(config, [
|
|
18
|
-
Object
|
|
19
|
-
], 'migrations/default', 'custom_migrations');
|
|
20
|
-
expect(options).toEqual({
|
|
21
|
-
type: 'postgres',
|
|
22
|
-
host: 'localhost',
|
|
23
|
-
port: 5432,
|
|
24
|
-
username: 'root',
|
|
25
|
-
password: 'pw',
|
|
26
|
-
database: 'flusys',
|
|
27
|
-
entities: [
|
|
28
|
-
Object
|
|
29
|
-
],
|
|
30
|
-
migrations: [
|
|
31
|
-
'migrations/default'
|
|
32
|
-
],
|
|
33
|
-
migrationsTableName: 'custom_migrations',
|
|
34
|
-
synchronize: false
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
it('should default database to an empty string and table name to "migrations"', ()=>{
|
|
38
|
-
const config = {
|
|
39
|
-
type: 'sqlite'
|
|
40
|
-
};
|
|
41
|
-
const options = (0, _datasourceconfigbuilder.buildDataSourceOptions)(config, [], 'migrations/default');
|
|
42
|
-
expect(options.database).toBe('');
|
|
43
|
-
expect(options.migrationsTableName).toBe('migrations');
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
describe('getMigrationsFolderPath', ()=>{
|
|
47
|
-
it('should use "default" when no tenantId is given', ()=>{
|
|
48
|
-
expect((0, _datasourceconfigbuilder.getMigrationsFolderPath)('migrations')).toBe('migrations/default');
|
|
49
|
-
});
|
|
50
|
-
it('should namespace the folder by tenant', ()=>{
|
|
51
|
-
expect((0, _datasourceconfigbuilder.getMigrationsFolderPath)('migrations', 'tenant-a')).toBe('migrations/tenant-a');
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
describe('getMigrationsGlobPattern', ()=>{
|
|
55
|
-
it('should build a glob matching ts and js migration files', ()=>{
|
|
56
|
-
expect((0, _datasourceconfigbuilder.getMigrationsGlobPattern)('migrations', 'tenant-a')).toBe('migrations/tenant-a/*{.ts,.js}');
|
|
57
|
-
});
|
|
58
|
-
it('should fall back to the default folder', ()=>{
|
|
59
|
-
expect((0, _datasourceconfigbuilder.getMigrationsGlobPattern)('migrations')).toBe('migrations/default/*{.ts,.js}');
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
describe('getDatabaseForTenant', ()=>{
|
|
63
|
-
const baseConfig = {
|
|
64
|
-
defaultDatabaseConfig: {
|
|
65
|
-
type: 'postgres',
|
|
66
|
-
database: 'flusys_default'
|
|
67
|
-
},
|
|
68
|
-
migrationsPath: 'migrations',
|
|
69
|
-
entities: []
|
|
70
|
-
};
|
|
71
|
-
it('should return the default tenant config when tenantId is omitted', ()=>{
|
|
72
|
-
const result = (0, _datasourceconfigbuilder.getDatabaseForTenant)(baseConfig);
|
|
73
|
-
expect(result.database).toBe('flusys_default');
|
|
74
|
-
expect(result.tenant).toBeUndefined();
|
|
75
|
-
expect(result.tenantConfig).toEqual({
|
|
76
|
-
id: 'default',
|
|
77
|
-
database: 'flusys_default',
|
|
78
|
-
enableCompanyFeature: false,
|
|
79
|
-
permissionMode: 'FULL'
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
it('should apply bootstrap feature flags to the default tenant config', ()=>{
|
|
83
|
-
const config = {
|
|
84
|
-
...baseConfig,
|
|
85
|
-
bootstrapAppConfig: {
|
|
86
|
-
databaseMode: 'single',
|
|
87
|
-
enableCompanyFeature: true,
|
|
88
|
-
permissionMode: 'RBAC'
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
const result = (0, _datasourceconfigbuilder.getDatabaseForTenant)(config);
|
|
92
|
-
expect(result.tenantConfig.enableCompanyFeature).toBe(true);
|
|
93
|
-
expect(result.tenantConfig.permissionMode).toBe('RBAC');
|
|
94
|
-
});
|
|
95
|
-
it('should resolve an explicit tenant by id', ()=>{
|
|
96
|
-
const config = {
|
|
97
|
-
...baseConfig,
|
|
98
|
-
tenants: [
|
|
99
|
-
{
|
|
100
|
-
id: 'tenant-a',
|
|
101
|
-
database: 'db_a'
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
id: 'tenant-b',
|
|
105
|
-
database: 'db_b'
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
|
-
};
|
|
109
|
-
const result = (0, _datasourceconfigbuilder.getDatabaseForTenant)(config, 'tenant-b');
|
|
110
|
-
expect(result.database).toBe('db_b');
|
|
111
|
-
expect(result.tenant?.id).toBe('tenant-b');
|
|
112
|
-
expect(result.tenantConfig.id).toBe('tenant-b');
|
|
113
|
-
});
|
|
114
|
-
it('should throw a descriptive error for an unknown tenant id', ()=>{
|
|
115
|
-
const config = {
|
|
116
|
-
...baseConfig,
|
|
117
|
-
tenants: [
|
|
118
|
-
{
|
|
119
|
-
id: 'tenant-a',
|
|
120
|
-
database: 'db_a'
|
|
121
|
-
}
|
|
122
|
-
]
|
|
123
|
-
};
|
|
124
|
-
expect(()=>(0, _datasourceconfigbuilder.getDatabaseForTenant)(config, 'unknown')).toThrow("Tenant 'unknown' not found. Available: tenant-a");
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
describe('resolveDatabaseConnection', ()=>{
|
|
128
|
-
const baseConfig = {
|
|
129
|
-
defaultDatabaseConfig: {
|
|
130
|
-
type: 'postgres',
|
|
131
|
-
host: 'default-host',
|
|
132
|
-
port: 5432,
|
|
133
|
-
username: 'default-user',
|
|
134
|
-
password: 'default-pw',
|
|
135
|
-
database: 'flusys_default'
|
|
136
|
-
},
|
|
137
|
-
migrationsPath: 'migrations',
|
|
138
|
-
entities: []
|
|
139
|
-
};
|
|
140
|
-
it('should return the default connection when no tenant matches', ()=>{
|
|
141
|
-
const result = (0, _datasourceconfigbuilder.resolveDatabaseConnection)(baseConfig);
|
|
142
|
-
expect(result).toEqual(baseConfig.defaultDatabaseConfig);
|
|
143
|
-
});
|
|
144
|
-
it('should overlay tenant-specific fields onto the default connection', ()=>{
|
|
145
|
-
const config = {
|
|
146
|
-
...baseConfig,
|
|
147
|
-
tenants: [
|
|
148
|
-
{
|
|
149
|
-
id: 'tenant-a',
|
|
150
|
-
database: 'db_a',
|
|
151
|
-
host: 'tenant-host'
|
|
152
|
-
}
|
|
153
|
-
]
|
|
154
|
-
};
|
|
155
|
-
const result = (0, _datasourceconfigbuilder.resolveDatabaseConnection)(config, 'tenant-a');
|
|
156
|
-
expect(result).toEqual({
|
|
157
|
-
type: 'postgres',
|
|
158
|
-
host: 'tenant-host',
|
|
159
|
-
port: 5432,
|
|
160
|
-
username: 'default-user',
|
|
161
|
-
password: 'default-pw',
|
|
162
|
-
database: 'db_a'
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
it('should fall back to default connection fields when tenant omits them', ()=>{
|
|
166
|
-
const config = {
|
|
167
|
-
...baseConfig,
|
|
168
|
-
tenants: [
|
|
169
|
-
{
|
|
170
|
-
id: 'tenant-a',
|
|
171
|
-
database: 'db_a'
|
|
172
|
-
}
|
|
173
|
-
]
|
|
174
|
-
};
|
|
175
|
-
const result = (0, _datasourceconfigbuilder.resolveDatabaseConnection)(config, 'tenant-a');
|
|
176
|
-
expect(result.host).toBe('default-host');
|
|
177
|
-
expect(result.port).toBe(5432);
|
|
178
|
-
expect(result.username).toBe('default-user');
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
describe('resolveEntities', ()=>{
|
|
182
|
-
it('should return the entities array directly when not a function', ()=>{
|
|
183
|
-
const config = {
|
|
184
|
-
entities: [
|
|
185
|
-
Object,
|
|
186
|
-
String
|
|
187
|
-
]
|
|
188
|
-
};
|
|
189
|
-
expect((0, _datasourceconfigbuilder.resolveEntities)(config)).toEqual([
|
|
190
|
-
Object,
|
|
191
|
-
String
|
|
192
|
-
]);
|
|
193
|
-
});
|
|
194
|
-
it('should invoke the resolver function with tenant config', ()=>{
|
|
195
|
-
const resolver = jest.fn().mockReturnValue([
|
|
196
|
-
Number
|
|
197
|
-
]);
|
|
198
|
-
const config = {
|
|
199
|
-
entities: resolver
|
|
200
|
-
};
|
|
201
|
-
const tenantConfig = {
|
|
202
|
-
id: 'tenant-a',
|
|
203
|
-
database: 'db_a'
|
|
204
|
-
};
|
|
205
|
-
const result = (0, _datasourceconfigbuilder.resolveEntities)(config, tenantConfig);
|
|
206
|
-
expect(resolver).toHaveBeenCalledWith(tenantConfig);
|
|
207
|
-
expect(result).toEqual([
|
|
208
|
-
Number
|
|
209
|
-
]);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
describe('getActiveTenants', ()=>{
|
|
213
|
-
it('should return an empty array when no tenants are configured', ()=>{
|
|
214
|
-
const config = {
|
|
215
|
-
tenants: undefined
|
|
216
|
-
};
|
|
217
|
-
expect((0, _datasourceconfigbuilder.getActiveTenants)(config)).toEqual([]);
|
|
218
|
-
});
|
|
219
|
-
it('should return the configured tenants', ()=>{
|
|
220
|
-
const tenants = [
|
|
221
|
-
{
|
|
222
|
-
id: 'a',
|
|
223
|
-
database: 'db_a'
|
|
224
|
-
}
|
|
225
|
-
];
|
|
226
|
-
const config = {
|
|
227
|
-
tenants
|
|
228
|
-
};
|
|
229
|
-
expect((0, _datasourceconfigbuilder.getActiveTenants)(config)).toBe(tenants);
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
});
|
package/cjs/utils/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
_export_star(require("./datasource-config.builder"), exports);
|
|
6
|
-
function _export_star(from, to) {
|
|
7
|
-
Object.keys(from).forEach(function(k) {
|
|
8
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
9
|
-
Object.defineProperty(to, k, {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
get: function() {
|
|
12
|
-
return from[k];
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
return from;
|
|
18
|
-
}
|