@node-c/core 1.0.0-alpha9 → 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.
Files changed (140) hide show
  1. package/coverage/src/app.ts.html +349 -0
  2. package/coverage/src/common/configProvider/configProvider.module.ts.html +160 -0
  3. package/coverage/src/common/configProvider/configProvider.service.ts.html +658 -0
  4. package/coverage/src/common/configProvider/index.html +146 -0
  5. package/coverage/src/common/configProvider/index.ts.html +94 -0
  6. package/coverage/src/common/definitions/common.constants.ts.html +160 -0
  7. package/coverage/src/common/definitions/common.errors.ts.html +124 -0
  8. package/coverage/src/common/definitions/index.html +146 -0
  9. package/coverage/src/common/definitions/index.ts.html +94 -0
  10. package/coverage/src/common/utils/index.html +131 -0
  11. package/coverage/src/common/utils/index.ts.html +88 -0
  12. package/coverage/src/common/utils/utils.loadDynamicModules.ts.html +265 -0
  13. package/coverage/src/domain/entityService/domain.entity.service.ts.html +784 -0
  14. package/coverage/src/domain/entityService/index.html +131 -0
  15. package/coverage/src/domain/entityService/index.ts.html +91 -0
  16. package/coverage/src/index.html +116 -0
  17. package/coverage/src/persistance/entityService/index.html +131 -0
  18. package/coverage/src/persistance/entityService/index.ts.html +91 -0
  19. package/coverage/src/persistance/entityService/persistance.entity.service.ts.html +268 -0
  20. package/dist/app.d.ts +2 -2
  21. package/dist/app.js +27 -15
  22. package/dist/app.js.map +1 -1
  23. package/dist/common/configProvider/configProvider.definitions.d.ts +156 -20
  24. package/dist/common/configProvider/configProvider.definitions.js +21 -7
  25. package/dist/common/configProvider/configProvider.definitions.js.map +1 -1
  26. package/dist/common/configProvider/configProvider.module.js +13 -2
  27. package/dist/common/configProvider/configProvider.module.js.map +1 -1
  28. package/dist/common/configProvider/configProvider.service.js +21 -18
  29. package/dist/common/configProvider/configProvider.service.js.map +1 -1
  30. package/dist/common/definitions/common.constants.d.ts +2 -1
  31. package/dist/common/definitions/common.constants.js +1 -0
  32. package/dist/common/definitions/common.constants.js.map +1 -1
  33. package/dist/common/utils/base64UrlEncode/base64UrlEncode.method.d.ts +1 -0
  34. package/dist/common/utils/base64UrlEncode/base64UrlEncode.method.js +9 -0
  35. package/dist/common/utils/base64UrlEncode/base64UrlEncode.method.js.map +1 -0
  36. package/dist/common/utils/base64UrlEncode/index.d.ts +1 -0
  37. package/dist/{persistance/entityService → common/utils/base64UrlEncode}/index.js +1 -2
  38. package/dist/common/utils/base64UrlEncode/index.js.map +1 -0
  39. package/dist/common/utils/getNested/getNested.definitions.d.ts +4 -0
  40. package/dist/common/utils/getNested/getNested.definitions.js +3 -0
  41. package/dist/common/utils/getNested/getNested.definitions.js.map +1 -0
  42. package/dist/common/utils/getNested/getNested.method.d.ts +6 -0
  43. package/dist/common/utils/getNested/getNested.method.js +88 -0
  44. package/dist/common/utils/getNested/getNested.method.js.map +1 -0
  45. package/dist/common/utils/getNested/index.d.ts +2 -0
  46. package/dist/common/utils/getNested/index.js +19 -0
  47. package/dist/common/utils/getNested/index.js.map +1 -0
  48. package/dist/common/utils/httpRequest/httpRequest.definitions.d.ts +19 -0
  49. package/dist/common/utils/httpRequest/httpRequest.definitions.js +3 -0
  50. package/dist/common/utils/httpRequest/httpRequest.definitions.js.map +1 -0
  51. package/dist/common/utils/httpRequest/httpRequest.method.d.ts +2 -0
  52. package/dist/common/utils/httpRequest/httpRequest.method.js +56 -0
  53. package/dist/common/utils/httpRequest/httpRequest.method.js.map +1 -0
  54. package/dist/common/utils/httpRequest/index.d.ts +2 -0
  55. package/dist/common/utils/httpRequest/index.js +19 -0
  56. package/dist/common/utils/httpRequest/index.js.map +1 -0
  57. package/dist/common/utils/index.d.ts +5 -1
  58. package/dist/common/utils/index.js +5 -1
  59. package/dist/common/utils/index.js.map +1 -1
  60. package/dist/common/utils/loadDynamicModules/index.d.ts +1 -0
  61. package/dist/common/utils/loadDynamicModules/index.js +18 -0
  62. package/dist/common/utils/loadDynamicModules/index.js.map +1 -0
  63. package/dist/common/utils/{utils.loadDynamicModules.d.ts → loadDynamicModules/utils.loadDynamicModules.d.ts} +1 -1
  64. package/dist/common/utils/loadDynamicModules/utils.loadDynamicModules.js.map +1 -0
  65. package/dist/common/utils/setNested/index.d.ts +2 -0
  66. package/dist/common/utils/setNested/index.js +19 -0
  67. package/dist/common/utils/setNested/index.js.map +1 -0
  68. package/dist/common/utils/setNested/setNested.definitions.d.ts +4 -0
  69. package/dist/common/utils/setNested/setNested.definitions.js +3 -0
  70. package/dist/common/utils/setNested/setNested.definitions.js.map +1 -0
  71. package/dist/common/utils/setNested/setNested.method.d.ts +2 -0
  72. package/dist/common/utils/setNested/setNested.method.js +70 -0
  73. package/dist/common/utils/setNested/setNested.method.js.map +1 -0
  74. package/dist/data/entityService/data.entity.service.d.ts +17 -0
  75. package/dist/data/entityService/data.entity.service.definitions.d.ts +117 -0
  76. package/dist/data/entityService/data.entity.service.definitions.js +28 -0
  77. package/dist/data/entityService/data.entity.service.definitions.js.map +1 -0
  78. package/dist/{persistance/entityService/persistance.entity.service.js → data/entityService/data.entity.service.js} +41 -9
  79. package/dist/data/entityService/data.entity.service.js.map +1 -0
  80. package/dist/data/entityService/index.d.ts +2 -0
  81. package/dist/data/entityService/index.js +19 -0
  82. package/dist/data/entityService/index.js.map +1 -0
  83. package/dist/domain/entityService/domain.entity.service.d.ts +28 -13
  84. package/dist/domain/entityService/domain.entity.service.definitions.d.ts +26 -19
  85. package/dist/domain/entityService/domain.entity.service.definitions.js +6 -6
  86. package/dist/domain/entityService/domain.entity.service.definitions.js.map +1 -1
  87. package/dist/domain/entityService/domain.entity.service.js +77 -69
  88. package/dist/domain/entityService/domain.entity.service.js.map +1 -1
  89. package/dist/index.d.ts +1 -1
  90. package/dist/index.js +1 -1
  91. package/dist/index.js.map +1 -1
  92. package/package.json +11 -10
  93. package/src/app.spec.ts +138 -0
  94. package/src/app.ts +115 -0
  95. package/src/common/configProvider/configProvider.definitions.ts +445 -0
  96. package/src/common/configProvider/configProvider.module.spec.ts +90 -0
  97. package/src/common/configProvider/configProvider.module.ts +25 -0
  98. package/src/common/configProvider/configProvider.service.spec.ts +206 -0
  99. package/src/common/configProvider/configProvider.service.ts +204 -0
  100. package/src/common/configProvider/index.ts +3 -0
  101. package/src/common/definitions/common.constants.ts +27 -0
  102. package/src/common/definitions/common.definitions.ts +13 -0
  103. package/src/common/definitions/common.errors.ts +13 -0
  104. package/src/common/definitions/index.ts +3 -0
  105. package/src/common/utils/base64UrlEncode/base64UrlEncode.method.ts +4 -0
  106. package/src/common/utils/base64UrlEncode/index.ts +1 -0
  107. package/src/common/utils/getNested/getNested.definitions.ts +4 -0
  108. package/src/common/utils/getNested/getNested.method.ts +108 -0
  109. package/src/common/utils/getNested/getNested.spec.ts +151 -0
  110. package/src/common/utils/getNested/index.ts +2 -0
  111. package/src/common/utils/httpRequest/httpRequest.definitions.ts +22 -0
  112. package/src/common/utils/httpRequest/httpRequest.method.ts +46 -0
  113. package/src/common/utils/httpRequest/index.ts +2 -0
  114. package/src/common/utils/index.ts +5 -0
  115. package/src/common/utils/loadDynamicModules/index.ts +1 -0
  116. package/src/common/utils/loadDynamicModules/utils.loadDynamicModules.spec.ts +111 -0
  117. package/src/common/utils/loadDynamicModules/utils.loadDynamicModules.ts +69 -0
  118. package/src/common/utils/setNested/index.ts +2 -0
  119. package/src/common/utils/setNested/setNested.definitions.ts +4 -0
  120. package/src/common/utils/setNested/setNested.method.ts +83 -0
  121. package/src/common/utils/setNested/setNested.spec.ts +184 -0
  122. package/src/data/entityService/data.entity.service.definitions.ts +154 -0
  123. package/src/data/entityService/data.entity.service.spec.ts +112 -0
  124. package/src/data/entityService/data.entity.service.ts +144 -0
  125. package/src/data/entityService/index.ts +2 -0
  126. package/src/domain/entityService/domain.entity.service.definitions.ts +142 -0
  127. package/src/domain/entityService/domain.entity.service.spec.ts +126 -0
  128. package/src/domain/entityService/domain.entity.service.ts +421 -0
  129. package/src/domain/entityService/index.ts +2 -0
  130. package/src/index.ts +6 -0
  131. package/src/vitest.config.ts +9 -0
  132. package/dist/common/utils/utils.loadDynamicModules.js.map +0 -1
  133. package/dist/persistance/entityService/index.d.ts +0 -2
  134. package/dist/persistance/entityService/index.js.map +0 -1
  135. package/dist/persistance/entityService/persistance.entity.service.d.ts +0 -11
  136. package/dist/persistance/entityService/persistance.entity.service.definitions.d.ts +0 -76
  137. package/dist/persistance/entityService/persistance.entity.service.definitions.js +0 -23
  138. package/dist/persistance/entityService/persistance.entity.service.definitions.js.map +0 -1
  139. package/dist/persistance/entityService/persistance.entity.service.js.map +0 -1
  140. /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,3 @@
1
+ export * from './configProvider.definitions';
2
+ export * from './configProvider.module';
3
+ export * from './configProvider.service';
@@ -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,3 @@
1
+ export * from './common.constants';
2
+ export * from './common.definitions';
3
+ export * from './common.errors';
@@ -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,4 @@
1
+ export interface GetNestedOptions {
2
+ arrayItemsShouldBeUnique?: boolean;
3
+ removeNestedFieldEscapeSign?: boolean;
4
+ }
@@ -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
+ }