@node-c/core 1.0.0-alpha8 → 1.0.0-beta0
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/coverage/src/app.ts.html +349 -0
- package/coverage/src/common/configProvider/configProvider.module.ts.html +160 -0
- package/coverage/src/common/configProvider/configProvider.service.ts.html +658 -0
- package/coverage/src/common/configProvider/index.html +146 -0
- package/coverage/src/common/configProvider/index.ts.html +94 -0
- package/coverage/src/common/definitions/common.constants.ts.html +160 -0
- package/coverage/src/common/definitions/common.errors.ts.html +124 -0
- package/coverage/src/common/definitions/index.html +146 -0
- package/coverage/src/common/definitions/index.ts.html +94 -0
- package/coverage/src/common/utils/index.html +131 -0
- package/coverage/src/common/utils/index.ts.html +88 -0
- package/coverage/src/common/utils/utils.loadDynamicModules.ts.html +265 -0
- package/coverage/src/domain/entityService/domain.entity.service.ts.html +784 -0
- package/coverage/src/domain/entityService/index.html +131 -0
- package/coverage/src/domain/entityService/index.ts.html +91 -0
- package/coverage/src/index.html +116 -0
- package/coverage/src/persistance/entityService/index.html +131 -0
- package/coverage/src/persistance/entityService/index.ts.html +91 -0
- package/coverage/src/persistance/entityService/persistance.entity.service.ts.html +268 -0
- package/dist/app.d.ts +2 -2
- package/dist/app.js +27 -15
- package/dist/app.js.map +1 -1
- package/dist/common/configProvider/configProvider.definitions.d.ts +156 -20
- package/dist/common/configProvider/configProvider.definitions.js +21 -7
- package/dist/common/configProvider/configProvider.definitions.js.map +1 -1
- package/dist/common/configProvider/configProvider.module.js +13 -2
- package/dist/common/configProvider/configProvider.module.js.map +1 -1
- package/dist/common/configProvider/configProvider.service.js +21 -18
- package/dist/common/configProvider/configProvider.service.js.map +1 -1
- package/dist/common/definitions/common.constants.d.ts +2 -1
- package/dist/common/definitions/common.constants.js +1 -0
- package/dist/common/definitions/common.constants.js.map +1 -1
- package/dist/common/utils/base64UrlEncode/base64UrlEncode.method.d.ts +1 -0
- package/dist/common/utils/base64UrlEncode/base64UrlEncode.method.js +9 -0
- package/dist/common/utils/base64UrlEncode/base64UrlEncode.method.js.map +1 -0
- package/dist/common/utils/base64UrlEncode/index.d.ts +1 -0
- package/dist/{persistance/common/entityService → common/utils/base64UrlEncode}/index.js +1 -2
- package/dist/common/utils/base64UrlEncode/index.js.map +1 -0
- package/dist/common/utils/getNested/getNested.definitions.d.ts +4 -0
- package/dist/common/utils/getNested/getNested.definitions.js +3 -0
- package/dist/common/utils/getNested/getNested.definitions.js.map +1 -0
- package/dist/common/utils/getNested/getNested.method.d.ts +6 -0
- package/dist/common/utils/getNested/getNested.method.js +88 -0
- package/dist/common/utils/getNested/getNested.method.js.map +1 -0
- package/dist/common/utils/getNested/index.d.ts +2 -0
- package/dist/common/utils/getNested/index.js +19 -0
- package/dist/common/utils/getNested/index.js.map +1 -0
- package/dist/common/utils/httpRequest/httpRequest.definitions.d.ts +19 -0
- package/dist/common/utils/httpRequest/httpRequest.definitions.js +3 -0
- package/dist/common/utils/httpRequest/httpRequest.definitions.js.map +1 -0
- package/dist/common/utils/httpRequest/httpRequest.method.d.ts +2 -0
- package/dist/common/utils/httpRequest/httpRequest.method.js +56 -0
- package/dist/common/utils/httpRequest/httpRequest.method.js.map +1 -0
- package/dist/common/utils/httpRequest/index.d.ts +2 -0
- package/dist/common/utils/httpRequest/index.js +19 -0
- package/dist/common/utils/httpRequest/index.js.map +1 -0
- package/dist/common/utils/index.d.ts +5 -1
- package/dist/common/utils/index.js +5 -1
- package/dist/common/utils/index.js.map +1 -1
- package/dist/common/utils/loadDynamicModules/index.d.ts +1 -0
- package/dist/common/utils/loadDynamicModules/index.js +18 -0
- package/dist/common/utils/loadDynamicModules/index.js.map +1 -0
- package/dist/common/utils/{utils.loadDynamicModules.d.ts → loadDynamicModules/utils.loadDynamicModules.d.ts} +1 -1
- package/dist/common/utils/loadDynamicModules/utils.loadDynamicModules.js.map +1 -0
- package/dist/common/utils/setNested/index.d.ts +2 -0
- package/dist/common/utils/setNested/index.js +19 -0
- package/dist/common/utils/setNested/index.js.map +1 -0
- package/dist/common/utils/setNested/setNested.definitions.d.ts +4 -0
- package/dist/common/utils/setNested/setNested.definitions.js +3 -0
- package/dist/common/utils/setNested/setNested.definitions.js.map +1 -0
- package/dist/common/utils/setNested/setNested.method.d.ts +2 -0
- package/dist/common/utils/setNested/setNested.method.js +70 -0
- package/dist/common/utils/setNested/setNested.method.js.map +1 -0
- package/dist/data/entityService/data.entity.service.d.ts +17 -0
- package/dist/data/entityService/data.entity.service.definitions.d.ts +117 -0
- package/dist/data/entityService/data.entity.service.definitions.js +28 -0
- package/dist/data/entityService/data.entity.service.definitions.js.map +1 -0
- package/dist/{persistance/common/entityService/persistance.entity.service.js → data/entityService/data.entity.service.js} +42 -10
- package/dist/data/entityService/data.entity.service.js.map +1 -0
- package/dist/data/entityService/index.d.ts +2 -0
- package/dist/data/entityService/index.js +19 -0
- package/dist/data/entityService/index.js.map +1 -0
- package/dist/domain/entityService/domain.entity.service.d.ts +28 -13
- package/dist/domain/entityService/domain.entity.service.definitions.d.ts +26 -17
- package/dist/domain/entityService/domain.entity.service.definitions.js +6 -6
- package/dist/domain/entityService/domain.entity.service.definitions.js.map +1 -1
- package/dist/domain/entityService/domain.entity.service.js +97 -56
- package/dist/domain/entityService/domain.entity.service.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +11 -10
- package/src/app.spec.ts +138 -0
- package/src/app.ts +115 -0
- package/src/common/configProvider/configProvider.definitions.ts +445 -0
- package/src/common/configProvider/configProvider.module.spec.ts +90 -0
- package/src/common/configProvider/configProvider.module.ts +25 -0
- package/src/common/configProvider/configProvider.service.spec.ts +206 -0
- package/src/common/configProvider/configProvider.service.ts +204 -0
- package/src/common/configProvider/index.ts +3 -0
- package/src/common/definitions/common.constants.ts +27 -0
- package/src/common/definitions/common.definitions.ts +13 -0
- package/src/common/definitions/common.errors.ts +13 -0
- package/src/common/definitions/index.ts +3 -0
- package/src/common/utils/base64UrlEncode/base64UrlEncode.method.ts +4 -0
- package/src/common/utils/base64UrlEncode/index.ts +1 -0
- package/src/common/utils/getNested/getNested.definitions.ts +4 -0
- package/src/common/utils/getNested/getNested.method.ts +108 -0
- package/src/common/utils/getNested/getNested.spec.ts +151 -0
- package/src/common/utils/getNested/index.ts +2 -0
- package/src/common/utils/httpRequest/httpRequest.definitions.ts +22 -0
- package/src/common/utils/httpRequest/httpRequest.method.ts +46 -0
- package/src/common/utils/httpRequest/index.ts +2 -0
- package/src/common/utils/index.ts +5 -0
- package/src/common/utils/loadDynamicModules/index.ts +1 -0
- package/src/common/utils/loadDynamicModules/utils.loadDynamicModules.spec.ts +111 -0
- package/src/common/utils/loadDynamicModules/utils.loadDynamicModules.ts +69 -0
- package/src/common/utils/setNested/index.ts +2 -0
- package/src/common/utils/setNested/setNested.definitions.ts +4 -0
- package/src/common/utils/setNested/setNested.method.ts +83 -0
- package/src/common/utils/setNested/setNested.spec.ts +184 -0
- package/src/data/entityService/data.entity.service.definitions.ts +154 -0
- package/src/data/entityService/data.entity.service.spec.ts +112 -0
- package/src/data/entityService/data.entity.service.ts +144 -0
- package/src/data/entityService/index.ts +2 -0
- package/src/domain/entityService/domain.entity.service.definitions.ts +142 -0
- package/src/domain/entityService/domain.entity.service.spec.ts +126 -0
- package/src/domain/entityService/domain.entity.service.ts +421 -0
- package/src/domain/entityService/index.ts +2 -0
- package/src/index.ts +6 -0
- package/src/vitest.config.ts +9 -0
- package/dist/common/utils/utils.loadDynamicModules.js.map +0 -1
- package/dist/persistance/common/entityService/index.d.ts +0 -2
- package/dist/persistance/common/entityService/index.js.map +0 -1
- package/dist/persistance/common/entityService/persistance.entity.service.d.ts +0 -11
- package/dist/persistance/common/entityService/persistance.entity.service.definitions.d.ts +0 -70
- package/dist/persistance/common/entityService/persistance.entity.service.definitions.js +0 -23
- package/dist/persistance/common/entityService/persistance.entity.service.definitions.js.map +0 -1
- package/dist/persistance/common/entityService/persistance.entity.service.js.map +0 -1
- /package/dist/common/utils/{utils.loadDynamicModules.js → loadDynamicModules/utils.loadDynamicModules.js} +0 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
import im from 'immutable';
|
|
5
|
+
import { mergeDeepRight } from 'ramda';
|
|
6
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
AppConfig,
|
|
10
|
+
AppConfigCommon,
|
|
11
|
+
AppEnvironment,
|
|
12
|
+
GenerateOrmconfigOptions,
|
|
13
|
+
LoadConfigAppConfigs
|
|
14
|
+
} from './configProvider.definitions';
|
|
15
|
+
import { ConfigProviderService } from './configProvider.service';
|
|
16
|
+
|
|
17
|
+
const BASE_PATH = path.join(__dirname, '../../../test');
|
|
18
|
+
|
|
19
|
+
describe('ConfigProviderService', () => {
|
|
20
|
+
describe('constructor', () => {
|
|
21
|
+
it('should assign the passed config to the service', () => {
|
|
22
|
+
const fakeConfig: AppConfig = {
|
|
23
|
+
api: {},
|
|
24
|
+
domain: {},
|
|
25
|
+
general: {
|
|
26
|
+
environment: AppEnvironment.Local,
|
|
27
|
+
projectName: 'TestProject',
|
|
28
|
+
projectRootPath: '/some/path',
|
|
29
|
+
projectVersion: '1.0.0'
|
|
30
|
+
},
|
|
31
|
+
data: {}
|
|
32
|
+
};
|
|
33
|
+
const service = new ConfigProviderService(fakeConfig);
|
|
34
|
+
expect(service.config).toEqual(fakeConfig);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// generateOrmconfig
|
|
39
|
+
describe('generateOrmconfig', () => {
|
|
40
|
+
const defaultFakeModuleName = 'db';
|
|
41
|
+
const defaultFakeOptions: GenerateOrmconfigOptions = {
|
|
42
|
+
entitiesPathInModule: 'entities',
|
|
43
|
+
migrationsPathInModule: 'migrations',
|
|
44
|
+
moduleName: defaultFakeModuleName,
|
|
45
|
+
modulePathInProject: 'src/data/db'
|
|
46
|
+
};
|
|
47
|
+
const defaultFakeConfig: AppConfig = {
|
|
48
|
+
api: {},
|
|
49
|
+
domain: {},
|
|
50
|
+
general: {
|
|
51
|
+
environment: AppEnvironment.Local,
|
|
52
|
+
projectName: 'TestProject',
|
|
53
|
+
projectRootPath: BASE_PATH,
|
|
54
|
+
projectVersion: '1.0.0'
|
|
55
|
+
},
|
|
56
|
+
data: {
|
|
57
|
+
[defaultFakeModuleName]: { type: 'mysql', extra: 'foo' }
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
let fakeModuleName: string;
|
|
61
|
+
let fakeOptions: GenerateOrmconfigOptions;
|
|
62
|
+
let fakeConfig: AppConfig;
|
|
63
|
+
let entitiesDirPath: string;
|
|
64
|
+
let migrationsPath: string;
|
|
65
|
+
let ormConfigPath: string;
|
|
66
|
+
// clean up before each test
|
|
67
|
+
beforeEach(async () => {
|
|
68
|
+
fakeModuleName = defaultFakeModuleName;
|
|
69
|
+
fakeOptions = im.fromJS(defaultFakeOptions).toJS() as unknown as GenerateOrmconfigOptions;
|
|
70
|
+
fakeConfig = im.fromJS(defaultFakeConfig).toJS() as unknown as AppConfig;
|
|
71
|
+
entitiesDirPath = path.join(BASE_PATH, fakeOptions.modulePathInProject, fakeOptions.entitiesPathInModule);
|
|
72
|
+
migrationsPath = path.join(BASE_PATH, fakeOptions.modulePathInProject, fakeOptions.migrationsPathInModule);
|
|
73
|
+
ormConfigPath = path.join(BASE_PATH, `ormconfig-${fakeModuleName}.json`);
|
|
74
|
+
try {
|
|
75
|
+
await fs.unlink(ormConfigPath);
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
77
|
+
} catch (_e: unknown) {}
|
|
78
|
+
});
|
|
79
|
+
it('should generate ormconfig file with entities and subscribers arrays correctly', async () => {
|
|
80
|
+
await ConfigProviderService.generateOrmconfig(fakeConfig, fakeOptions);
|
|
81
|
+
const expectedEntities = [
|
|
82
|
+
path.join(entitiesDirPath, 'orders', 'orders.entity.ts'),
|
|
83
|
+
path.join(entitiesDirPath, 'users', 'users.entity.ts')
|
|
84
|
+
];
|
|
85
|
+
const expectedSubscribers = [
|
|
86
|
+
path.join(entitiesDirPath, 'orders', 'orders.subscriber.ts'),
|
|
87
|
+
path.join(entitiesDirPath, 'users', 'users.subscriber.ts')
|
|
88
|
+
];
|
|
89
|
+
const expectedMergedConfig = mergeDeepRight(fakeConfig.data[fakeModuleName], {
|
|
90
|
+
entities: expectedEntities,
|
|
91
|
+
subscribers: expectedSubscribers,
|
|
92
|
+
migrations: [`${migrationsPath}/**/*.ts`],
|
|
93
|
+
cli: { migrationsDir: migrationsPath }
|
|
94
|
+
});
|
|
95
|
+
expect((await fs.readFile(ormConfigPath)).toString()).toEqual(JSON.stringify(expectedMergedConfig));
|
|
96
|
+
});
|
|
97
|
+
it('should handle case with empty entities directory (no folders)', async () => {
|
|
98
|
+
fakeOptions.entitiesPathInModule = 'entitiesEmpty';
|
|
99
|
+
await ConfigProviderService.generateOrmconfig(fakeConfig, fakeOptions);
|
|
100
|
+
const expectedMergedConfig = mergeDeepRight(fakeConfig.data[fakeModuleName], {
|
|
101
|
+
entities: [],
|
|
102
|
+
subscribers: [],
|
|
103
|
+
migrations: [`${migrationsPath}/**/*.ts`],
|
|
104
|
+
cli: { migrationsDir: migrationsPath }
|
|
105
|
+
});
|
|
106
|
+
expect((await fs.readFile(ormConfigPath)).toString()).toEqual(JSON.stringify(expectedMergedConfig));
|
|
107
|
+
});
|
|
108
|
+
it('should ignore files that do not match entity or subscriber patterns', async () => {
|
|
109
|
+
await ConfigProviderService.generateOrmconfig(fakeConfig, fakeOptions);
|
|
110
|
+
const writtenConfig = JSON.parse((await fs.readFile(ormConfigPath)).toString());
|
|
111
|
+
expect(writtenConfig.entities).toEqual(
|
|
112
|
+
expect.arrayContaining([
|
|
113
|
+
path.join(entitiesDirPath, 'users', 'users.entity.ts'),
|
|
114
|
+
path.join(entitiesDirPath, 'orders', 'orders.entity.ts')
|
|
115
|
+
])
|
|
116
|
+
);
|
|
117
|
+
expect(writtenConfig.entities).not.toEqual(
|
|
118
|
+
expect.arrayContaining([
|
|
119
|
+
path.join(entitiesDirPath, 'misc', 'note.txt'),
|
|
120
|
+
path.join(entitiesDirPath, 'misc', 'helper.ts')
|
|
121
|
+
])
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
it('should propagate error if fs.readdir fails', async () => {
|
|
125
|
+
fakeOptions.entitiesPathInModule = 'entitiesNotExistent';
|
|
126
|
+
await expect(ConfigProviderService.generateOrmconfig(fakeConfig, fakeOptions)).rejects.toThrow('ENOENT');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// loadConfig
|
|
131
|
+
describe('loadConfig', () => {
|
|
132
|
+
const defaultAppConfigs: LoadConfigAppConfigs = {
|
|
133
|
+
appConfigCommon: {
|
|
134
|
+
general: {
|
|
135
|
+
projectName: 'TestProject',
|
|
136
|
+
projectRootPath: BASE_PATH,
|
|
137
|
+
projectVersion: '1.0.0'
|
|
138
|
+
},
|
|
139
|
+
api: {
|
|
140
|
+
test: {}
|
|
141
|
+
},
|
|
142
|
+
domain: {},
|
|
143
|
+
data: {}
|
|
144
|
+
},
|
|
145
|
+
appConfigProfileLocal: {
|
|
146
|
+
general: { environment: AppEnvironment.Local }
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const defaultEnvKeys = {
|
|
150
|
+
API: {
|
|
151
|
+
HTTP: { HOST: 'hostname', PORT: 'port' }
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
const defaultEnvKeysParentNames = {
|
|
155
|
+
API: {
|
|
156
|
+
children: { TEST: 'test' },
|
|
157
|
+
name: 'api'
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
let appConfigs: LoadConfigAppConfigs;
|
|
161
|
+
|
|
162
|
+
beforeEach(() => {
|
|
163
|
+
process.env.NODE_ENV = 'local';
|
|
164
|
+
appConfigs = im.fromJS(defaultAppConfigs).toJS() as unknown as LoadConfigAppConfigs;
|
|
165
|
+
});
|
|
166
|
+
it('should load and merge configuration from env file and populate nested values', async () => {
|
|
167
|
+
delete process.env['NODE_ENV'];
|
|
168
|
+
const loadedConfig = await ConfigProviderService.loadConfig(appConfigs, {
|
|
169
|
+
envKeys: defaultEnvKeys,
|
|
170
|
+
envKeysParentNames: defaultEnvKeysParentNames
|
|
171
|
+
});
|
|
172
|
+
expect(loadedConfig.general.environment).toBe('local');
|
|
173
|
+
expect(loadedConfig.api).toBeDefined();
|
|
174
|
+
expect(loadedConfig.api.rest).toBeUndefined();
|
|
175
|
+
expect(loadedConfig.api.test).toBeDefined();
|
|
176
|
+
expect(loadedConfig.api.test.hostname).toBe('127.0.0.1');
|
|
177
|
+
expect(loadedConfig.api.test.port).toBe('3000');
|
|
178
|
+
});
|
|
179
|
+
it('should use default envKeys and envKeysParentNames if options not provided', async () => {
|
|
180
|
+
appConfigs.appConfigCommon.api!.rest = {};
|
|
181
|
+
const loadedConfig = await ConfigProviderService.loadConfig(appConfigs);
|
|
182
|
+
expect(loadedConfig.general.environment).toBe('local');
|
|
183
|
+
expect(loadedConfig.api.rest).toBeDefined();
|
|
184
|
+
expect(loadedConfig.api.rest.hostname).toBe('localhost');
|
|
185
|
+
expect(loadedConfig.api.test.hostname).toBeUndefined();
|
|
186
|
+
});
|
|
187
|
+
it('should throw an error if the .env file is missing', async () => {
|
|
188
|
+
(appConfigs.appConfigCommon as AppConfigCommon).general.projectRootPath = '/fake/path';
|
|
189
|
+
await expect(
|
|
190
|
+
ConfigProviderService.loadConfig(appConfigs, {
|
|
191
|
+
envKeys: defaultEnvKeys,
|
|
192
|
+
envKeysParentNames: defaultEnvKeysParentNames
|
|
193
|
+
})
|
|
194
|
+
).rejects.toThrow('ENOENT');
|
|
195
|
+
});
|
|
196
|
+
it('should skip env keys that do not match expected pattern', async () => {
|
|
197
|
+
const loadedConfig = await ConfigProviderService.loadConfig(appConfigs, {
|
|
198
|
+
envKeys: defaultEnvKeys,
|
|
199
|
+
envKeysParentNames: defaultEnvKeysParentNames
|
|
200
|
+
});
|
|
201
|
+
expect(loadedConfig.api.test.hostname).toBe('127.0.0.1');
|
|
202
|
+
expect(loadedConfig.api.test.port).toBe('3000');
|
|
203
|
+
expect(loadedConfig.api.something).toBeUndefined();
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
});
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
5
|
+
import dotenv from 'dotenv';
|
|
6
|
+
import ld from 'lodash';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
APP_CONFIG_FROM_ENV_KEYS as APP_CONFIG_FROM_ENV_KEYS_DEFAULT,
|
|
10
|
+
APP_CONFIG_FROM_ENV_KEYS_PARENT_NAMES as APP_CONFIG_FROM_ENV_KEYS_PARENT_NAMES_DEFAULT,
|
|
11
|
+
AppConfigDataRDB,
|
|
12
|
+
AppConfig as AppConfigDefault,
|
|
13
|
+
AppEnvironment,
|
|
14
|
+
GenerateOrmconfigOptions,
|
|
15
|
+
LoadConfigAppConfigs,
|
|
16
|
+
LoadConfigOptions,
|
|
17
|
+
RDBType
|
|
18
|
+
} from './configProvider.definitions';
|
|
19
|
+
|
|
20
|
+
import { Constants } from '../definitions';
|
|
21
|
+
import { setNested } from '../utils';
|
|
22
|
+
|
|
23
|
+
@Injectable()
|
|
24
|
+
export class ConfigProviderService<AppConfig extends AppConfigDefault = AppConfigDefault> {
|
|
25
|
+
constructor(
|
|
26
|
+
@Inject(Constants.CONFIG)
|
|
27
|
+
// eslint-disable-next-line no-unused-vars
|
|
28
|
+
public config: AppConfig
|
|
29
|
+
) {}
|
|
30
|
+
|
|
31
|
+
// TODO: consider moving this into the data-rdb package
|
|
32
|
+
static async generateOrmconfig<AppConfig extends AppConfigDefault = AppConfigDefault>(
|
|
33
|
+
config: AppConfig,
|
|
34
|
+
options: GenerateOrmconfigOptions
|
|
35
|
+
): Promise<void> {
|
|
36
|
+
const {
|
|
37
|
+
general: { projectRootPath },
|
|
38
|
+
data
|
|
39
|
+
} = config;
|
|
40
|
+
const { entitiesPathInModule, migrationsPathInModule, moduleName, modulePathInProject, seedsPathInModule } =
|
|
41
|
+
options;
|
|
42
|
+
const entitiesDirPath = path.join(projectRootPath, modulePathInProject, entitiesPathInModule);
|
|
43
|
+
const entitiesDirData = await fs.readdir(entitiesDirPath);
|
|
44
|
+
const entities: string[] = [];
|
|
45
|
+
const migrationsPath = path.join(projectRootPath, modulePathInProject, migrationsPathInModule);
|
|
46
|
+
const moduleConfig = data[moduleName] as AppConfigDataRDB;
|
|
47
|
+
const subscribers: string[] = [];
|
|
48
|
+
for (const i in entitiesDirData) {
|
|
49
|
+
const entityName = entitiesDirData[i];
|
|
50
|
+
if (entityName.match(/^base$/)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const entityFolderPath = path.join(entitiesDirPath, entityName);
|
|
54
|
+
const entityFolderChildItemStat = await fs.lstat(entityFolderPath);
|
|
55
|
+
if (!entityFolderChildItemStat.isDirectory()) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const entityFolderData = await fs.readdir(entityFolderPath);
|
|
59
|
+
for (const j in entityFolderData) {
|
|
60
|
+
const entityFolderFileName = entityFolderData[j];
|
|
61
|
+
if (entityFolderFileName.match(/\.entity\./)) {
|
|
62
|
+
entities.push(path.join(entityFolderPath, entityFolderFileName));
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (entityFolderFileName.match(/\.subscriber\./)) {
|
|
66
|
+
subscribers.push(path.join(entityFolderPath, entityFolderFileName));
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// write the ORM Config file
|
|
72
|
+
const ormconfigMigrations: string[] = [`${migrationsPath}/**/*.ts`];
|
|
73
|
+
if (seedsPathInModule) {
|
|
74
|
+
const baseSeedsPath = path.join(projectRootPath, modulePathInProject, seedsPathInModule);
|
|
75
|
+
ormconfigMigrations.push(`${baseSeedsPath}/common/**/*.ts`);
|
|
76
|
+
}
|
|
77
|
+
await fs.writeFile(
|
|
78
|
+
path.join(projectRootPath, `ormconfig-${moduleName}.json`),
|
|
79
|
+
JSON.stringify(
|
|
80
|
+
ld.merge(data[moduleName], {
|
|
81
|
+
cli: {
|
|
82
|
+
migrationsDir: migrationsPath
|
|
83
|
+
},
|
|
84
|
+
entities: [...entities],
|
|
85
|
+
migrations: ormconfigMigrations,
|
|
86
|
+
name: moduleConfig.connectionName,
|
|
87
|
+
synchronize: moduleConfig.type === RDBType.Aurora ? true : false,
|
|
88
|
+
subscribers: [...subscribers],
|
|
89
|
+
type: moduleConfig.type === RDBType.Aurora ? RDBType.MySQL : moduleConfig.type,
|
|
90
|
+
...(moduleConfig.typeormExtraOptions || {})
|
|
91
|
+
})
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
// write the Datasource file
|
|
95
|
+
const entitiesPathInProject = path.join(modulePathInProject, entitiesPathInModule);
|
|
96
|
+
await fs.writeFile(
|
|
97
|
+
path.join(projectRootPath, `datasource-${moduleName}.ts`),
|
|
98
|
+
"import { loadDynamicModules } from '@node-c/core';\n" +
|
|
99
|
+
'\n' +
|
|
100
|
+
"import { DataSource } from 'typeorm';\n" +
|
|
101
|
+
'\n' +
|
|
102
|
+
`import * as FolderData from './${entitiesPathInProject}';\n` +
|
|
103
|
+
'\n' +
|
|
104
|
+
'const { entities } = loadDynamicModules(FolderData);\n' +
|
|
105
|
+
'\n' +
|
|
106
|
+
'export default new DataSource({\n' +
|
|
107
|
+
` database: '${moduleConfig.database}',\n` +
|
|
108
|
+
' entities: entities as unknown as string[],\n' +
|
|
109
|
+
` host: '${moduleConfig.host}',\n` +
|
|
110
|
+
' logging: false,\n' +
|
|
111
|
+
` migrations: [${ormconfigMigrations.map(item => `'${item.replace(projectRootPath.replace(/\/$/, ''), '.')}'`).join(', ')}],\n` +
|
|
112
|
+
` name: '${moduleConfig.connectionName}',\n` +
|
|
113
|
+
` password: '${moduleConfig.password}',\n` +
|
|
114
|
+
` port: ${moduleConfig.port},\n` +
|
|
115
|
+
' subscribers: [],\n' +
|
|
116
|
+
` synchronize: ${moduleConfig.type === RDBType.Aurora ? true : false},\n` +
|
|
117
|
+
` type: '${moduleConfig.type === RDBType.Aurora ? RDBType.MySQL : moduleConfig.type}',\n` +
|
|
118
|
+
` username: '${moduleConfig.user}'\n` +
|
|
119
|
+
'});\n'
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// TODO: logging about invalid config values
|
|
124
|
+
static async loadConfig<AppConfig extends AppConfigDefault = AppConfigDefault>(
|
|
125
|
+
appConfigs: LoadConfigAppConfigs,
|
|
126
|
+
options?: LoadConfigOptions
|
|
127
|
+
): Promise<AppConfig> {
|
|
128
|
+
const { useEnvFile, useEnvFileWithPriority, ...optionsData } = options || ({} as LoadConfigOptions);
|
|
129
|
+
const envKeys = optionsData.envKeys || APP_CONFIG_FROM_ENV_KEYS_DEFAULT;
|
|
130
|
+
const envKeysParentNames = optionsData.envKeysParentNames || APP_CONFIG_FROM_ENV_KEYS_PARENT_NAMES_DEFAULT;
|
|
131
|
+
const processEnv = process.env;
|
|
132
|
+
const envName = (processEnv['NODE_ENV'] as AppEnvironment) || AppEnvironment.Local;
|
|
133
|
+
const config = ld.merge(
|
|
134
|
+
appConfigs.appConfigCommon,
|
|
135
|
+
appConfigs[
|
|
136
|
+
`appConfigProfile${envName.charAt(0).toUpperCase()}${envName.substring(
|
|
137
|
+
1,
|
|
138
|
+
envName.length
|
|
139
|
+
)}` as keyof typeof appConfigs
|
|
140
|
+
]
|
|
141
|
+
) as AppConfig;
|
|
142
|
+
const moduleNamesByCategoryAndType: {
|
|
143
|
+
[moduleCategory: string]: { [moduleType: string]: string[] };
|
|
144
|
+
} = {};
|
|
145
|
+
const moduleTypesRegex = new RegExp(`^((${Object.keys(envKeys).join(')|(')}))_`);
|
|
146
|
+
config.general.environment = envName;
|
|
147
|
+
let envVars: Record<string, unknown> = processEnv;
|
|
148
|
+
if (useEnvFile) {
|
|
149
|
+
// populate the data from the .env file into the config object
|
|
150
|
+
const envVarsFromFile = dotenv.parse(
|
|
151
|
+
(await fs.readFile(path.join(config.general.projectRootPath, `envFiles/.${envName}.env`))).toString()
|
|
152
|
+
);
|
|
153
|
+
if (useEnvFileWithPriority) {
|
|
154
|
+
envVars = ld.merge(envVars, envVarsFromFile);
|
|
155
|
+
} else {
|
|
156
|
+
envVars = ld.merge(envVarsFromFile, envVars);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// first pass - create a list of modules by name and map them by module type
|
|
160
|
+
for (const envKey in envVars) {
|
|
161
|
+
const [, moduleCategory] = envKey.match(moduleTypesRegex) || [];
|
|
162
|
+
if (!moduleCategory) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const [, moduleName] = envKey.match(new RegExp(`^${moduleCategory}_(.+)_MODULE_TYPE$`)) || [];
|
|
166
|
+
if (!moduleName) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const moduleFields = envKeys[moduleCategory as keyof typeof envKeys];
|
|
170
|
+
const moduleType = envVars[envKey] as keyof typeof moduleFields;
|
|
171
|
+
if (!moduleFields[moduleType]) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (!moduleNamesByCategoryAndType[moduleCategory]) {
|
|
175
|
+
moduleNamesByCategoryAndType[moduleCategory] = {};
|
|
176
|
+
}
|
|
177
|
+
if (!moduleNamesByCategoryAndType[moduleCategory][moduleType]) {
|
|
178
|
+
moduleNamesByCategoryAndType[moduleCategory][moduleType] = [];
|
|
179
|
+
}
|
|
180
|
+
moduleNamesByCategoryAndType[moduleCategory][moduleType].push(moduleName);
|
|
181
|
+
}
|
|
182
|
+
// second pass - actually go through the env vars and populate them in the config accordingly
|
|
183
|
+
for (const moduleCategory in moduleNamesByCategoryAndType) {
|
|
184
|
+
const { children: moduleConfigKeysForCategory, name: categoryConfigKey } = envKeysParentNames[moduleCategory];
|
|
185
|
+
const moduleFieldsForCategory = envKeys[moduleCategory as keyof typeof envKeys];
|
|
186
|
+
const moduleNamesByType = moduleNamesByCategoryAndType[moduleCategory];
|
|
187
|
+
for (const moduleType in moduleNamesByType) {
|
|
188
|
+
const moduleFieldsForType = moduleFieldsForCategory[
|
|
189
|
+
moduleType as keyof typeof moduleFieldsForCategory
|
|
190
|
+
] as Record<string, string>;
|
|
191
|
+
const moduleNames = moduleNamesByType[moduleType];
|
|
192
|
+
moduleNames.forEach(moduleName => {
|
|
193
|
+
const moduleConfigKey = moduleConfigKeysForCategory[moduleName];
|
|
194
|
+
for (const fieldName in moduleFieldsForType) {
|
|
195
|
+
const configKey = `${categoryConfigKey}.${moduleConfigKey}.${moduleFieldsForType[fieldName]}`;
|
|
196
|
+
const envKey = `${moduleCategory}_${moduleName}_${fieldName}`;
|
|
197
|
+
setNested(config, configKey, envVars[envKey]);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return config;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export enum Constants {
|
|
2
|
+
// eslint-disable-next-line no-unused-vars
|
|
3
|
+
API_MODULE_NAME = 'API_MODULE_NAME',
|
|
4
|
+
// eslint-disable-next-line no-unused-vars
|
|
5
|
+
CONFIG = 'CONFIG',
|
|
6
|
+
// eslint-disable-next-line no-unused-vars
|
|
7
|
+
DOMAIN_MODULE_NAME = 'DOMAIN_MODULE_NAME',
|
|
8
|
+
// eslint-disable-next-line no-unused-vars
|
|
9
|
+
DATA_MODULE_NAME = 'DATA_MODULE_NAME'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export enum HttpMethod {
|
|
13
|
+
// eslint-disable-next-line no-unused-vars
|
|
14
|
+
DELETE = 'delete',
|
|
15
|
+
// eslint-disable-next-line no-unused-vars
|
|
16
|
+
GET = 'get',
|
|
17
|
+
// eslint-disable-next-line no-unused-vars
|
|
18
|
+
HEAD = 'head',
|
|
19
|
+
// eslint-disable-next-line no-unused-vars
|
|
20
|
+
OPTIONS = 'options',
|
|
21
|
+
// eslint-disable-next-line no-unused-vars
|
|
22
|
+
PATCH = 'patch',
|
|
23
|
+
// eslint-disable-next-line no-unused-vars
|
|
24
|
+
POST = 'post',
|
|
25
|
+
// eslint-disable-next-line no-unused-vars
|
|
26
|
+
PUT = 'put'
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface GenericObject<Values = unknown> {
|
|
2
|
+
[fieldName: string]: Values;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export class GenericObjectClass<Values = unknown> implements GenericObject<Values> {
|
|
6
|
+
[fieldName: string]: Values;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type GenericObjectType<Type> =
|
|
10
|
+
| {
|
|
11
|
+
new (): Type;
|
|
12
|
+
}
|
|
13
|
+
| ((..._args: unknown[]) => unknown);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GenericObject } from './common.definitions';
|
|
2
|
+
|
|
3
|
+
export class ApplicationError implements Error {
|
|
4
|
+
data?: { errorCode?: number } | GenericObject;
|
|
5
|
+
message: string;
|
|
6
|
+
name: string;
|
|
7
|
+
|
|
8
|
+
constructor(message: string, data?: GenericObject) {
|
|
9
|
+
this.message = message;
|
|
10
|
+
this.name = 'ApplicationError';
|
|
11
|
+
this.data = data || {};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export const base64UrlEncode = (buffer: ArrayBuffer | string): string => {
|
|
2
|
+
const actualBuffer = typeof buffer === 'string' ? Buffer.from(buffer, 'utf-8') : Buffer.from(buffer);
|
|
3
|
+
return actualBuffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
4
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './base64UrlEncode.method';
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { GetNestedOptions } from './getNested.definitions';
|
|
2
|
+
|
|
3
|
+
import { GenericObject } from '../../definitions';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extracts a value from a deeply nested object, for example foo.bar.0.baz from {foo: {bar: [{baz: 'test'}]}}.
|
|
7
|
+
* @param parent (required) - The object to retrieve the value from.
|
|
8
|
+
* @param field (required) - The path to the field.
|
|
9
|
+
* @param options (optional) - Method executiom options, such as arrayItemsShouldBeUnique.
|
|
10
|
+
* @returns The decoded object or value.
|
|
11
|
+
*/
|
|
12
|
+
export function getNested<ReturnData = unknown>(
|
|
13
|
+
parent: unknown | unknown[],
|
|
14
|
+
field: string,
|
|
15
|
+
options?: GetNestedOptions
|
|
16
|
+
): { paths: string[]; unifiedValue?: ReturnData | ReturnData[]; values: (ReturnData | undefined)[] } {
|
|
17
|
+
if (typeof parent !== 'object' || parent === null || !field.length) {
|
|
18
|
+
return { paths: [field], values: [] };
|
|
19
|
+
}
|
|
20
|
+
const { arrayItemsShouldBeUnique, removeNestedFieldEscapeSign } = options || {};
|
|
21
|
+
const fieldData = field.split('.');
|
|
22
|
+
const fieldDataLength = fieldData.length;
|
|
23
|
+
const paths: string[] = [];
|
|
24
|
+
const values: (ReturnData | undefined)[] = [];
|
|
25
|
+
let currentElement = parent;
|
|
26
|
+
let currentPath = '';
|
|
27
|
+
for (let i = 0; i < fieldDataLength; i++) {
|
|
28
|
+
let innerElementName = fieldData[i];
|
|
29
|
+
// logic for handling Sequelize-style $foo.bar$ - should be treated as a single element
|
|
30
|
+
if (innerElementName.charAt(0) === '$') {
|
|
31
|
+
let closingBracketFound = false,
|
|
32
|
+
closingBracketIndex = i + 1;
|
|
33
|
+
while (closingBracketIndex < fieldDataLength) {
|
|
34
|
+
const element = fieldData[closingBracketIndex];
|
|
35
|
+
// false alarm - there's another $ opening before the current one closed - so the current one must be just a variable name, not a bracket
|
|
36
|
+
if (element.charAt(0) === '$') {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
// found it !
|
|
40
|
+
if (element.charAt(element.length - 1) === '$') {
|
|
41
|
+
closingBracketFound = true;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
closingBracketIndex++;
|
|
45
|
+
}
|
|
46
|
+
if (closingBracketFound) {
|
|
47
|
+
for (let j = i + 1; j <= closingBracketIndex; j++) {
|
|
48
|
+
innerElementName += `.${fieldData[j]}`;
|
|
49
|
+
}
|
|
50
|
+
i = closingBracketIndex;
|
|
51
|
+
if (removeNestedFieldEscapeSign) {
|
|
52
|
+
innerElementName = innerElementName.replace(/^\$/, '').replace(/\$$/, '');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const nextElement = (currentElement as GenericObject)[innerElementName];
|
|
57
|
+
currentPath += `${currentPath.length ? '.' : ''}${innerElementName}`;
|
|
58
|
+
if (typeof nextElement === 'undefined') {
|
|
59
|
+
paths.push(currentPath);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
// if the next element is an array, prepare to return an array of the inner items
|
|
63
|
+
if (nextElement instanceof Array) {
|
|
64
|
+
// if this is the last item, just return the array
|
|
65
|
+
if (i === fieldDataLength - 1) {
|
|
66
|
+
paths.push(currentPath);
|
|
67
|
+
values.push(nextElement as ReturnData);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
// if the next item is not an index, recursively call self for each item of the array
|
|
71
|
+
if (isNaN(parseInt(fieldData[i + 1], 10))) {
|
|
72
|
+
let innerPath = '';
|
|
73
|
+
for (let j = i + 1; j < fieldDataLength; j++) {
|
|
74
|
+
innerPath += `${fieldData[j]}${j < fieldDataLength - 1 ? '.' : ''}`;
|
|
75
|
+
}
|
|
76
|
+
nextElement.forEach(item => {
|
|
77
|
+
const { paths: innerPaths, values: innerValue } = getNested(item, innerPath, options);
|
|
78
|
+
if (typeof innerValue === 'undefined') {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// flatten inner arrays
|
|
82
|
+
innerValue.forEach((innerValueItem, innerValueItemIndex) => {
|
|
83
|
+
if (
|
|
84
|
+
!arrayItemsShouldBeUnique ||
|
|
85
|
+
(arrayItemsShouldBeUnique && values.indexOf(innerValueItem as ReturnData) === -1)
|
|
86
|
+
) {
|
|
87
|
+
paths.push(`${currentPath}.${innerValueItemIndex}.${innerPaths[innerValueItemIndex]}`);
|
|
88
|
+
values.push(innerValueItem as ReturnData);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
currentElement = nextElement as GenericObject;
|
|
96
|
+
if (i === fieldDataLength - 1) {
|
|
97
|
+
paths.push(currentPath);
|
|
98
|
+
values.push(currentElement as ReturnData);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
let unifiedValue = undefined;
|
|
102
|
+
if (paths.length > 1 || values.length > 1) {
|
|
103
|
+
unifiedValue = values;
|
|
104
|
+
} else {
|
|
105
|
+
unifiedValue = values[0];
|
|
106
|
+
}
|
|
107
|
+
return { paths, unifiedValue: unifiedValue as ReturnData | ReturnData[], values };
|
|
108
|
+
}
|