@ruiapp/rapid-core 0.8.20 → 0.9.0

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 (191) hide show
  1. package/CHANGELOG.md +11 -11
  2. package/dist/core/request.d.ts +1 -0
  3. package/dist/core/server.d.ts +2 -1
  4. package/dist/index.js +746 -741
  5. package/dist/server.d.ts +2 -1
  6. package/package.json +1 -1
  7. package/rollup.config.js +16 -16
  8. package/src/bootstrapApplicationConfig.ts +782 -782
  9. package/src/core/actionHandler.ts +23 -23
  10. package/src/core/eventManager.ts +20 -20
  11. package/src/core/facility.ts +7 -7
  12. package/src/core/http/formDataParser.ts +89 -89
  13. package/src/core/http-types.ts +4 -4
  14. package/src/core/pluginManager.ts +184 -184
  15. package/src/core/providers/runtimeProvider.ts +5 -5
  16. package/src/core/request.ts +96 -95
  17. package/src/core/response.ts +79 -79
  18. package/src/core/routeContext.ts +127 -127
  19. package/src/core/routesBuilder.ts +90 -90
  20. package/src/core/server.ts +152 -151
  21. package/src/dataAccess/columnTypeMapper.ts +22 -22
  22. package/src/dataAccess/dataAccessTypes.ts +165 -165
  23. package/src/dataAccess/dataAccessor.ts +135 -135
  24. package/src/dataAccess/entityManager.ts +1932 -1932
  25. package/src/dataAccess/entityMapper.ts +111 -111
  26. package/src/dataAccess/entityValidator.ts +33 -26
  27. package/src/dataAccess/propertyMapper.ts +28 -28
  28. package/src/deno-std/assert/assert.ts +9 -9
  29. package/src/deno-std/assert/assertion_error.ts +7 -7
  30. package/src/deno-std/datetime/to_imf.ts +32 -32
  31. package/src/deno-std/encoding/base64.ts +141 -141
  32. package/src/deno-std/http/cookie.ts +372 -372
  33. package/src/facilities/cache/CacheFacilityTypes.ts +29 -29
  34. package/src/facilities/cache/CacheFactory.ts +31 -31
  35. package/src/facilities/cache/MemoryCache.ts +58 -58
  36. package/src/facilities/cache/MemoryCacheProvider.ts +15 -15
  37. package/src/facilities/log/LogFacility.ts +35 -35
  38. package/src/helpers/entityHelpers.ts +76 -76
  39. package/src/helpers/filterHelper.ts +148 -148
  40. package/src/helpers/inputHelper.ts +11 -11
  41. package/src/helpers/licenseHelper.ts +29 -29
  42. package/src/helpers/metaHelper.ts +111 -111
  43. package/src/helpers/runCollectionEntityActionHandler.ts +58 -58
  44. package/src/index.ts +76 -76
  45. package/src/plugins/auth/AuthPlugin.ts +93 -93
  46. package/src/plugins/auth/actionHandlers/changePassword.ts +61 -61
  47. package/src/plugins/auth/actionHandlers/createSession.ts +67 -67
  48. package/src/plugins/auth/actionHandlers/deleteSession.ts +18 -18
  49. package/src/plugins/auth/actionHandlers/getMyProfile.ts +35 -35
  50. package/src/plugins/auth/actionHandlers/index.ts +8 -8
  51. package/src/plugins/auth/actionHandlers/resetPassword.ts +45 -45
  52. package/src/plugins/auth/models/AccessToken.ts +56 -56
  53. package/src/plugins/auth/models/index.ts +3 -3
  54. package/src/plugins/auth/routes/changePassword.ts +15 -15
  55. package/src/plugins/auth/routes/getMyProfile.ts +15 -15
  56. package/src/plugins/auth/routes/index.ts +7 -7
  57. package/src/plugins/auth/routes/resetPassword.ts +15 -15
  58. package/src/plugins/auth/routes/signin.ts +15 -15
  59. package/src/plugins/auth/routes/signout.ts +15 -15
  60. package/src/plugins/auth/services/AuthService.ts +39 -39
  61. package/src/plugins/cronJob/CronJobPlugin.ts +104 -104
  62. package/src/plugins/cronJob/CronJobPluginTypes.ts +44 -44
  63. package/src/plugins/cronJob/actionHandlers/index.ts +4 -4
  64. package/src/plugins/cronJob/actionHandlers/runCronJob.ts +32 -32
  65. package/src/plugins/cronJob/entityWatchers/cronJobEntityWatchers.ts +24 -24
  66. package/src/plugins/cronJob/entityWatchers/index.ts +4 -4
  67. package/src/plugins/cronJob/models/CronJob.ts +129 -129
  68. package/src/plugins/cronJob/models/index.ts +3 -3
  69. package/src/plugins/cronJob/routes/index.ts +3 -3
  70. package/src/plugins/cronJob/routes/runCronJob.ts +15 -15
  71. package/src/plugins/cronJob/services/CronJobService.ts +252 -252
  72. package/src/plugins/dataManage/DataManagePlugin.ts +163 -163
  73. package/src/plugins/dataManage/actionHandlers/addEntityRelations.ts +15 -15
  74. package/src/plugins/dataManage/actionHandlers/countCollectionEntities.ts +17 -17
  75. package/src/plugins/dataManage/actionHandlers/createCollectionEntitiesBatch.ts +81 -81
  76. package/src/plugins/dataManage/actionHandlers/createCollectionEntity.ts +20 -20
  77. package/src/plugins/dataManage/actionHandlers/deleteCollectionEntities.ts +45 -45
  78. package/src/plugins/dataManage/actionHandlers/deleteCollectionEntityById.ts +20 -20
  79. package/src/plugins/dataManage/actionHandlers/findCollectionEntities.ts +27 -27
  80. package/src/plugins/dataManage/actionHandlers/findCollectionEntityById.ts +30 -30
  81. package/src/plugins/dataManage/actionHandlers/queryDatabase.ts +22 -22
  82. package/src/plugins/dataManage/actionHandlers/removeEntityRelations.ts +15 -15
  83. package/src/plugins/dataManage/actionHandlers/updateCollectionEntityById.ts +38 -38
  84. package/src/plugins/entityAccessControl/EntityAccessControlPlugin.ts +146 -146
  85. package/src/plugins/fileManage/FileManagePlugin.ts +52 -52
  86. package/src/plugins/fileManage/actionHandlers/downloadDocument.ts +65 -65
  87. package/src/plugins/fileManage/actionHandlers/downloadFile.ts +44 -44
  88. package/src/plugins/fileManage/actionHandlers/uploadFile.ts +33 -33
  89. package/src/plugins/fileManage/routes/downloadDocument.ts +15 -15
  90. package/src/plugins/fileManage/routes/downloadFile.ts +15 -15
  91. package/src/plugins/fileManage/routes/index.ts +5 -5
  92. package/src/plugins/fileManage/routes/uploadFile.ts +15 -15
  93. package/src/plugins/license/LicensePlugin.ts +79 -79
  94. package/src/plugins/license/LicensePluginTypes.ts +95 -95
  95. package/src/plugins/license/LicenseService.ts +118 -118
  96. package/src/plugins/license/actionHandlers/getLicense.ts +18 -18
  97. package/src/plugins/license/actionHandlers/index.ts +4 -4
  98. package/src/plugins/license/helpers/certHelper.ts +21 -21
  99. package/src/plugins/license/helpers/cryptoHelper.ts +47 -47
  100. package/src/plugins/license/models/index.ts +1 -1
  101. package/src/plugins/license/routes/getLicense.ts +15 -15
  102. package/src/plugins/license/routes/index.ts +3 -3
  103. package/src/plugins/mail/MailPlugin.ts +74 -74
  104. package/src/plugins/mail/MailPluginTypes.ts +27 -27
  105. package/src/plugins/mail/MailService.ts +38 -38
  106. package/src/plugins/mail/actionHandlers/index.ts +3 -3
  107. package/src/plugins/mail/models/index.ts +1 -1
  108. package/src/plugins/mail/routes/index.ts +1 -1
  109. package/src/plugins/metaManage/MetaManagePlugin.ts +198 -198
  110. package/src/plugins/metaManage/actionHandlers/getMetaModelDetail.ts +10 -10
  111. package/src/plugins/metaManage/actionHandlers/listMetaModels.ts +9 -9
  112. package/src/plugins/metaManage/actionHandlers/listMetaRoutes.ts +9 -9
  113. package/src/plugins/metaManage/services/MetaService.ts +376 -376
  114. package/src/plugins/notification/NotificationPlugin.ts +68 -68
  115. package/src/plugins/notification/NotificationPluginTypes.ts +13 -13
  116. package/src/plugins/notification/NotificationService.ts +25 -25
  117. package/src/plugins/notification/actionHandlers/index.ts +3 -3
  118. package/src/plugins/notification/models/Notification.ts +60 -60
  119. package/src/plugins/notification/models/index.ts +3 -3
  120. package/src/plugins/notification/routes/index.ts +1 -1
  121. package/src/plugins/routeManage/RouteManagePlugin.ts +62 -62
  122. package/src/plugins/routeManage/actionHandlers/httpProxy.ts +13 -13
  123. package/src/plugins/sequence/SequencePlugin.ts +146 -146
  124. package/src/plugins/sequence/SequencePluginTypes.ts +69 -69
  125. package/src/plugins/sequence/SequenceService.ts +92 -92
  126. package/src/plugins/sequence/actionHandlers/generateSn.ts +32 -32
  127. package/src/plugins/sequence/actionHandlers/index.ts +4 -4
  128. package/src/plugins/sequence/models/SequenceAutoIncrementRecord.ts +49 -49
  129. package/src/plugins/sequence/models/SequenceRule.ts +42 -42
  130. package/src/plugins/sequence/models/index.ts +4 -4
  131. package/src/plugins/sequence/routes/generateSn.ts +15 -15
  132. package/src/plugins/sequence/routes/index.ts +3 -3
  133. package/src/plugins/sequence/segment-utility.ts +11 -11
  134. package/src/plugins/sequence/segments/autoIncrement.ts +90 -90
  135. package/src/plugins/sequence/segments/dayOfMonth.ts +19 -19
  136. package/src/plugins/sequence/segments/index.ts +9 -9
  137. package/src/plugins/sequence/segments/literal.ts +16 -16
  138. package/src/plugins/sequence/segments/month.ts +19 -19
  139. package/src/plugins/sequence/segments/parameter.ts +20 -20
  140. package/src/plugins/sequence/segments/year.ts +19 -19
  141. package/src/plugins/serverOperation/ServerOperationPlugin.ts +91 -91
  142. package/src/plugins/serverOperation/ServerOperationPluginTypes.ts +15 -15
  143. package/src/plugins/serverOperation/actionHandlers/index.ts +4 -4
  144. package/src/plugins/serverOperation/actionHandlers/runServerOperation.ts +15 -15
  145. package/src/plugins/setting/SettingPlugin.ts +68 -68
  146. package/src/plugins/setting/SettingPluginTypes.ts +37 -37
  147. package/src/plugins/setting/SettingService.ts +213 -213
  148. package/src/plugins/setting/actionHandlers/getSystemSettingValues.ts +30 -30
  149. package/src/plugins/setting/actionHandlers/getUserSettingValues.ts +38 -38
  150. package/src/plugins/setting/actionHandlers/index.ts +6 -6
  151. package/src/plugins/setting/actionHandlers/setSystemSettingValues.ts +30 -30
  152. package/src/plugins/setting/models/SystemSettingGroupSetting.ts +57 -57
  153. package/src/plugins/setting/models/SystemSettingItem.ts +48 -48
  154. package/src/plugins/setting/models/SystemSettingItemSetting.ts +73 -73
  155. package/src/plugins/setting/models/UserSettingGroupSetting.ts +57 -57
  156. package/src/plugins/setting/models/UserSettingItem.ts +55 -55
  157. package/src/plugins/setting/models/UserSettingItemSetting.ts +73 -73
  158. package/src/plugins/setting/models/index.ts +8 -8
  159. package/src/plugins/setting/routes/getSystemSettingValues.ts +15 -15
  160. package/src/plugins/setting/routes/getUserSettingValues.ts +15 -15
  161. package/src/plugins/setting/routes/index.ts +5 -5
  162. package/src/plugins/setting/routes/setSystemSettingValues.ts +15 -15
  163. package/src/plugins/stateMachine/StateMachinePlugin.ts +196 -196
  164. package/src/plugins/stateMachine/StateMachinePluginTypes.ts +48 -48
  165. package/src/plugins/stateMachine/actionHandlers/index.ts +4 -4
  166. package/src/plugins/stateMachine/actionHandlers/sendStateMachineEvent.ts +54 -54
  167. package/src/plugins/stateMachine/models/StateMachine.ts +42 -42
  168. package/src/plugins/stateMachine/models/index.ts +3 -3
  169. package/src/plugins/stateMachine/routes/index.ts +3 -3
  170. package/src/plugins/stateMachine/routes/sendStateMachineEvent.ts +15 -15
  171. package/src/plugins/stateMachine/stateMachineHelper.ts +36 -36
  172. package/src/plugins/webhooks/WebhooksPlugin.ts +148 -148
  173. package/src/plugins/webhooks/pluginConfig.ts +75 -75
  174. package/src/polyfill.ts +5 -5
  175. package/src/proxy/mod.ts +38 -38
  176. package/src/proxy/types.ts +21 -21
  177. package/src/queryBuilder/index.ts +1 -1
  178. package/src/queryBuilder/queryBuilder.ts +755 -755
  179. package/src/server.ts +523 -524
  180. package/src/types/cron-job-types.ts +66 -66
  181. package/src/types.ts +832 -832
  182. package/src/utilities/accessControlUtility.ts +33 -33
  183. package/src/utilities/entityUtility.ts +18 -18
  184. package/src/utilities/errorUtility.ts +15 -15
  185. package/src/utilities/fsUtility.ts +61 -61
  186. package/src/utilities/httpUtility.ts +19 -19
  187. package/src/utilities/jwtUtility.ts +26 -26
  188. package/src/utilities/pathUtility.ts +14 -14
  189. package/src/utilities/timeUtility.ts +17 -17
  190. package/src/utilities/typeUtility.ts +15 -15
  191. package/tsconfig.json +19 -19
package/src/server.ts CHANGED
@@ -1,524 +1,523 @@
1
- import DataAccessor from "./dataAccess/dataAccessor";
2
- import {
3
- GetDataAccessorOptions,
4
- GetModelOptions,
5
- IDatabaseAccessor,
6
- IDatabaseConfig,
7
- IQueryBuilder,
8
- IRpdDataAccessor,
9
- RpdApplicationConfig,
10
- RpdDataModel,
11
- RpdServerEventTypes,
12
- RapidServerConfig,
13
- RpdDataModelProperty,
14
- CreateEntityOptions,
15
- UpdateEntityByIdOptions,
16
- EntityWatchHandlerContext,
17
- EntityWatcherType,
18
- RpdEntityCreateEventPayload,
19
- EmitServerEventOptions,
20
- IDatabaseClient,
21
- } from "./types";
22
-
23
- import QueryBuilder from "./queryBuilder/queryBuilder";
24
- import PluginManager from "./core/pluginManager";
25
- import EventManager from "./core/eventManager";
26
- import { ActionHandler, ActionHandlerContext, IPluginActionHandler } from "./core/actionHandler";
27
- import { IRpdServer, RapidPlugin } from "./core/server";
28
- import { buildRoutes } from "./core/routesBuilder";
29
- import { Next, RouteContext } from "./core/routeContext";
30
- import { RapidRequest } from "./core/request";
31
- import bootstrapApplicationConfig from "./bootstrapApplicationConfig";
32
- import EntityManager from "./dataAccess/entityManager";
33
- import { bind, cloneDeep, find, forEach, isString, merge, omit } from "lodash";
34
- import { Logger } from "./facilities/log/LogFacility";
35
- import { FacilityFactory } from "./core/facility";
36
- import { CronJobConfiguration } from "./types/cron-job-types";
37
-
38
- export interface InitServerOptions {
39
- logger: Logger;
40
- databaseAccessor: IDatabaseAccessor;
41
- databaseConfig: IDatabaseConfig;
42
- serverConfig: RapidServerConfig;
43
- applicationConfig?: RpdApplicationConfig;
44
- facilityFactories?: FacilityFactory[];
45
- plugins?: RapidPlugin[];
46
- entityWatchers?: EntityWatcherType[];
47
-
48
- /**
49
- * Application level cron jobs.
50
- */
51
- cronJobs?: CronJobConfiguration[];
52
-
53
- /**
54
- * All cron jobs of the server will be disabled if set `true`.
55
- */
56
- disableCronJobs?: boolean;
57
- }
58
-
59
- export class RapidServer implements IRpdServer {
60
- #logger: Logger;
61
- #facilityFactories: Map<string, FacilityFactory>;
62
- #pluginManager: PluginManager;
63
- #plugins: RapidPlugin[];
64
- #eventManager: EventManager<RpdServerEventTypes>;
65
- #middlewares: any[];
66
- #bootstrapApplicationConfig: RpdApplicationConfig;
67
- #applicationConfig: RpdApplicationConfig;
68
- #actionHandlersMapByCode: Map<string, ActionHandler>;
69
- #databaseAccessor: IDatabaseAccessor;
70
- #cachedDataAccessors: Map<string, DataAccessor>;
71
-
72
- #entityBeforeCreateEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
73
- #entityCreateEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
74
- #entityBeforeUpdateEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
75
- #entityUpdateEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
76
- #entityBeforeDeleteEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
77
- #entityDeleteEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
78
- #entityAddRelationsEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
79
- #entityRemoveRelationsEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
80
- #entityBeforeResponseEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
81
- #entityWatchers: EntityWatcherType[];
82
- #appEntityWatchers: EntityWatcherType[];
83
-
84
- #cronJobs: CronJobConfiguration[];
85
- #appCronJobs: CronJobConfiguration[];
86
- #disableCronJobs: boolean;
87
-
88
- #cachedEntityManager: Map<string, EntityManager>;
89
- #services: Map<string, any>;
90
- queryBuilder: IQueryBuilder;
91
- config: RapidServerConfig;
92
- databaseConfig: IDatabaseConfig;
93
- #buildedRoutes: (ctx: any, next: any) => any;
94
-
95
- constructor(options: InitServerOptions) {
96
- this.#logger = options.logger;
97
-
98
- this.#facilityFactories = new Map();
99
- if (options.facilityFactories) {
100
- forEach(options.facilityFactories, (factory) => {
101
- this.registerFacilityFactory(factory);
102
- });
103
- }
104
-
105
- this.#pluginManager = new PluginManager(this);
106
- this.#eventManager = new EventManager();
107
- this.#middlewares = [];
108
- this.#bootstrapApplicationConfig = options.applicationConfig || bootstrapApplicationConfig;
109
-
110
- this.#applicationConfig = {} as RpdApplicationConfig;
111
- this.#actionHandlersMapByCode = new Map();
112
- this.#databaseAccessor = options.databaseAccessor;
113
- this.#cachedDataAccessors = new Map();
114
- this.#cachedEntityManager = new Map();
115
-
116
- this.#entityBeforeCreateEventEmitters = new EventManager();
117
- this.#entityCreateEventEmitters = new EventManager();
118
- this.#entityBeforeUpdateEventEmitters = new EventManager();
119
- this.#entityUpdateEventEmitters = new EventManager();
120
- this.#entityBeforeDeleteEventEmitters = new EventManager();
121
- this.#entityDeleteEventEmitters = new EventManager();
122
- this.#entityAddRelationsEventEmitters = new EventManager();
123
- this.#entityRemoveRelationsEventEmitters = new EventManager();
124
- this.#entityBeforeResponseEventEmitters = new EventManager();
125
-
126
- this.registerEventHandler("entity.beforeCreate", this.#handleEntityEvent.bind(this, "entity.beforeCreate"));
127
- this.registerEventHandler("entity.create", this.#handleEntityEvent.bind(this, "entity.create"));
128
- this.registerEventHandler("entity.beforeUpdate", this.#handleEntityEvent.bind(this, "entity.beforeUpdate"));
129
- this.registerEventHandler("entity.update", this.#handleEntityEvent.bind(this, "entity.update"));
130
- this.registerEventHandler("entity.beforeDelete", this.#handleEntityEvent.bind(this, "entity.beforeDelete"));
131
- this.registerEventHandler("entity.delete", this.#handleEntityEvent.bind(this, "entity.delete"));
132
- this.registerEventHandler("entity.addRelations", this.#handleEntityEvent.bind(this, "entity.addRelations"));
133
- this.registerEventHandler("entity.removeRelations", this.#handleEntityEvent.bind(this, "entity.removeRelations"));
134
- this.registerEventHandler("entity.beforeResponse", this.#handleEntityEvent.bind(this, "entity.beforeResponse"));
135
-
136
- this.#entityWatchers = [];
137
- this.#appEntityWatchers = options.entityWatchers || [];
138
-
139
- this.#cronJobs = [];
140
- this.#appCronJobs = options.cronJobs || [];
141
- this.#disableCronJobs = !!options.disableCronJobs;
142
-
143
- this.#services = new Map();
144
-
145
- this.queryBuilder = new QueryBuilder({
146
- dbDefaultSchema: options.databaseConfig.dbDefaultSchema,
147
- });
148
- this.databaseConfig = options.databaseConfig;
149
- this.config = options.serverConfig;
150
-
151
- this.#plugins = options.plugins || [];
152
- }
153
-
154
- getLogger(): Logger {
155
- return this.#logger;
156
- }
157
-
158
- getApplicationConfig() {
159
- return this.#applicationConfig;
160
- }
161
-
162
- getDatabaseAccessor(): IDatabaseAccessor {
163
- return this.#databaseAccessor;
164
- }
165
-
166
- appendApplicationConfig(config: Partial<RpdApplicationConfig>) {
167
- const { models, dataDictionaries, routes } = config;
168
- if (models) {
169
- for (const model of models) {
170
- const originalModel = find(this.#applicationConfig.models, (item) => item.singularCode == model.singularCode);
171
- if (originalModel) {
172
- merge(
173
- originalModel,
174
- omit(model, ["id", "maintainedBy", "namespace", "singularCode", "pluralCode", "schema", "tableName", "properties", "extensions"]),
175
- );
176
- originalModel.name = model.name;
177
- const originalProperties = originalModel.properties;
178
- for (const property of model.properties) {
179
- const originalProperty = find(originalProperties, (item) => item.code == property.code);
180
- if (originalProperty) {
181
- originalProperty.name = property.name;
182
- } else {
183
- originalProperties.push(property);
184
- }
185
- }
186
- } else {
187
- this.#applicationConfig.models.push(model);
188
- }
189
- }
190
- }
191
-
192
- if (dataDictionaries) {
193
- for (const dataDictionary of dataDictionaries) {
194
- const originalDataDictionary = find(this.#applicationConfig.dataDictionaries, (item) => item.code == dataDictionary.code);
195
- if (originalDataDictionary) {
196
- originalDataDictionary.name = dataDictionary.name;
197
- originalDataDictionary.description = dataDictionary.description;
198
- originalDataDictionary.entries = dataDictionary.entries;
199
- } else {
200
- this.#applicationConfig.dataDictionaries.push(dataDictionary);
201
- }
202
- }
203
- }
204
- if (routes) {
205
- for (const route of routes) {
206
- const originalRoute = find(this.#applicationConfig.routes, (item) => item.code == route.code);
207
- if (originalRoute) {
208
- originalRoute.name = route.name;
209
- originalRoute.actions = route.actions;
210
- } else {
211
- this.#applicationConfig.routes.push(route);
212
- }
213
- }
214
- }
215
- }
216
-
217
- appendModelProperties(modelSingularCode: string, properties: RpdDataModelProperty[]) {
218
- const originalModel = find(this.#applicationConfig.models, (item) => item.singularCode == modelSingularCode);
219
- if (!originalModel) {
220
- throw new Error(`Cannot append model properties. Model '${modelSingularCode}' was not found.`);
221
- }
222
-
223
- const originalProperties = originalModel.properties;
224
- for (const property of properties) {
225
- const originalProperty = find(originalProperties, (item) => item.code == property.code);
226
- if (originalProperty) {
227
- originalProperty.name = property.name;
228
- } else {
229
- originalProperties.push(property);
230
- }
231
- }
232
- }
233
-
234
- registerActionHandler(plugin: RapidPlugin, options: IPluginActionHandler) {
235
- const handler = bind(options.handler, null, plugin);
236
- this.#actionHandlersMapByCode.set(options.code, handler);
237
- }
238
-
239
- getActionHandlerByCode(code: string) {
240
- return this.#actionHandlersMapByCode.get(code);
241
- }
242
-
243
- registerMiddleware(middleware: any) {
244
- this.#middlewares.push(middleware);
245
- }
246
-
247
- getDataAccessor<T = any>(options: GetDataAccessorOptions): IRpdDataAccessor<T> {
248
- const { namespace, singularCode } = options;
249
-
250
- let dataAccessor = this.#cachedDataAccessors.get(singularCode);
251
- if (dataAccessor) {
252
- return dataAccessor;
253
- }
254
-
255
- const model = this.getModel(options);
256
- if (!model) {
257
- throw new Error(`Data model ${namespace}.${singularCode} not found.`);
258
- }
259
-
260
- dataAccessor = new DataAccessor<T>(this, this.#databaseAccessor, {
261
- model,
262
- queryBuilder: this.queryBuilder as QueryBuilder,
263
- });
264
- this.#cachedDataAccessors.set(singularCode, dataAccessor);
265
- return dataAccessor;
266
- }
267
-
268
- getModel(options: GetModelOptions): RpdDataModel | undefined {
269
- if (options.namespace) {
270
- return this.#applicationConfig?.models.find((e) => e.namespace === options.namespace && e.singularCode === options.singularCode);
271
- }
272
-
273
- return this.#applicationConfig?.models.find((e) => e.singularCode === options.singularCode);
274
- }
275
-
276
- getEntityManager<TEntity = any>(singularCode: string): EntityManager<TEntity> {
277
- let entityManager = this.#cachedEntityManager.get(singularCode);
278
- if (entityManager) {
279
- return entityManager;
280
- }
281
-
282
- const dataAccessor = this.getDataAccessor({ singularCode });
283
- entityManager = new EntityManager(this, dataAccessor);
284
- this.#cachedEntityManager.set(singularCode, entityManager);
285
- return entityManager;
286
- }
287
-
288
- registerEventHandler<K extends keyof RpdServerEventTypes>(eventName: K, listener: (...args: RpdServerEventTypes[K]) => void) {
289
- this.#eventManager.on(eventName, listener);
290
- }
291
-
292
- registerEntityWatcher(entityWatcher: EntityWatcherType) {
293
- this.#entityWatchers.push(entityWatcher);
294
- }
295
-
296
- async emitEvent<TEventName extends keyof RpdServerEventTypes>(event: EmitServerEventOptions<TEventName>) {
297
- const { eventName, payload, sender, routeContext: routerContext } = event;
298
- this.#logger.debug(`Emitting '${eventName}' event.`, { eventName });
299
- this.#logger.verbose(`Event payload: `, { payload });
300
- await this.#eventManager.emit<TEventName>(eventName, sender, payload as any, routerContext);
301
-
302
- // TODO: should move this logic into metaManager
303
- // if (
304
- // (eventName === "entity.create" || eventName === "entity.update" ||
305
- // eventName === "entity.delete") &&
306
- // payload.namespace === "meta"
307
- // ) {
308
- // await this.configureApplication();
309
- // }
310
- }
311
-
312
- registerService(name: string, service: any) {
313
- this.#services.set(name, service);
314
- }
315
-
316
- getService<TService>(name: string): TService {
317
- return this.#services.get(name);
318
- }
319
-
320
- registerCronJob(job: CronJobConfiguration): void {
321
- const jobDuplicate = find(this.#cronJobs, (item: CronJobConfiguration) => item.code === job.code);
322
- if (jobDuplicate) {
323
- this.#logger.warn(`Duplicated cron job with code "${job.code}"`);
324
- }
325
-
326
- this.#cronJobs.push(job);
327
- }
328
-
329
- listCronJobs() {
330
- return [...this.#cronJobs, ...this.#appCronJobs];
331
- }
332
-
333
- async start() {
334
- this.#logger.info("Starting rapid server...");
335
- const pluginManager = this.#pluginManager;
336
- await pluginManager.loadPlugins(this.#plugins);
337
-
338
- await pluginManager.initPlugins();
339
-
340
- await pluginManager.registerMiddlewares();
341
- await pluginManager.registerActionHandlers();
342
- await pluginManager.registerEventHandlers();
343
- await pluginManager.registerMessageHandlers();
344
- await pluginManager.registerTaskProcessors();
345
-
346
- this.#entityWatchers = this.#entityWatchers.concat(this.#appEntityWatchers);
347
- for (const entityWatcher of this.#entityWatchers) {
348
- if (entityWatcher.eventName === "entity.beforeCreate") {
349
- this.#entityBeforeCreateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
350
- } else if (entityWatcher.eventName === "entity.create") {
351
- this.#entityCreateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
352
- } else if (entityWatcher.eventName === "entity.beforeUpdate") {
353
- this.#entityBeforeUpdateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
354
- } else if (entityWatcher.eventName === "entity.update") {
355
- this.#entityUpdateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
356
- } else if (entityWatcher.eventName === "entity.beforeDelete") {
357
- this.#entityBeforeDeleteEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
358
- } else if (entityWatcher.eventName === "entity.delete") {
359
- this.#entityDeleteEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
360
- } else if (entityWatcher.eventName === "entity.addRelations") {
361
- this.#entityAddRelationsEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
362
- } else if (entityWatcher.eventName === "entity.removeRelations") {
363
- this.#entityRemoveRelationsEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
364
- } else if (entityWatcher.eventName === "entity.beforeResponse") {
365
- this.#entityBeforeResponseEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
366
- }
367
- }
368
-
369
- await this.configureApplication();
370
-
371
- if (!this.#disableCronJobs) {
372
- await pluginManager.registerCronJobs();
373
- }
374
-
375
- this.#logger.info(`Rapid server ready.`);
376
- await pluginManager.onApplicationReady(this.#applicationConfig);
377
- }
378
-
379
- async configureApplication() {
380
- this.#applicationConfig = cloneDeep(this.#bootstrapApplicationConfig) as RpdApplicationConfig;
381
-
382
- const pluginManager = this.#pluginManager;
383
- await pluginManager.onLoadingApplication(this.#applicationConfig);
384
- await pluginManager.configureModels(this.#applicationConfig);
385
- await pluginManager.configureModelProperties(this.#applicationConfig);
386
- await pluginManager.configureServices(this.#applicationConfig);
387
- await pluginManager.configureRoutes(this.#applicationConfig);
388
-
389
- // TODO: check application configuration.
390
-
391
- await pluginManager.onApplicationLoaded(this.#applicationConfig);
392
-
393
- this.#buildedRoutes = await buildRoutes(this, this.#applicationConfig);
394
- }
395
-
396
- registerFacilityFactory(factory: FacilityFactory) {
397
- this.#facilityFactories.set(factory.name, factory);
398
- }
399
-
400
- async getFacility<TFacility = any, TOptions = any>(name: string, options?: TOptions, nullIfUnknownFacility?: boolean): Promise<TFacility> {
401
- const factory = this.#facilityFactories.get(name);
402
- if (!factory) {
403
- if (nullIfUnknownFacility) {
404
- return null;
405
- } else {
406
- throw new Error(`Failed to get facility. Unknown facility name: ${name}`);
407
- }
408
- }
409
-
410
- return await factory.createFacility(this, options);
411
- }
412
-
413
- async queryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient, dropErrorLog?: boolean): Promise<any[]> {
414
- try {
415
- return await this.#databaseAccessor.queryDatabaseObject(sql, params, client);
416
- } catch (err) {
417
- if (!dropErrorLog) {
418
- this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
419
- }
420
- throw err;
421
- }
422
- }
423
-
424
- async tryQueryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient, dropErrorLog?: boolean): Promise<any[]> {
425
- try {
426
- return await this.queryDatabaseObject(sql, params, client);
427
- } catch (err) {
428
- if (!dropErrorLog) {
429
- this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
430
- }
431
- }
432
-
433
- return [];
434
- }
435
-
436
- get middlewares() {
437
- return this.#middlewares;
438
- }
439
-
440
- async handleRequest(request: Request, next: Next) {
441
- const rapidRequest = new RapidRequest(this, request);
442
- await rapidRequest.parseBody();
443
- const routeContext: RouteContext = new RouteContext(this, rapidRequest);
444
- const { response } = routeContext;
445
-
446
- try {
447
- await this.#pluginManager.onPrepareRouteContext(routeContext);
448
- await this.#buildedRoutes(routeContext, next);
449
- } catch (ex) {
450
- let error: any;
451
- if (isString(ex)) {
452
- error = {
453
- message: ex,
454
- };
455
- } else {
456
- error = { name: ex.name, message: ex.message, stack: ex.stack };
457
- }
458
- this.#logger.error("handle request error.", { error });
459
- response.json({ error }, 500);
460
- }
461
-
462
- if (!response.status && !response.body) {
463
- response.json(
464
- {
465
- error: {
466
- message: "No route handler was found to handle this request.",
467
- },
468
- },
469
- 404,
470
- );
471
- }
472
- return response.getResponse();
473
- }
474
-
475
- async beforeRunRouteActions(handlerContext: ActionHandlerContext) {
476
- await this.#pluginManager.beforeRunRouteActions(handlerContext);
477
- }
478
-
479
- async beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions) {
480
- await this.#pluginManager.beforeCreateEntity(model, options);
481
- }
482
-
483
- async beforeUpdateEntity(model: RpdDataModel, options: UpdateEntityByIdOptions, currentEntity: any) {
484
- await this.#pluginManager.beforeUpdateEntity(model, options, currentEntity);
485
- }
486
-
487
- async #handleEntityEvent(eventName: keyof RpdServerEventTypes, sender: RapidPlugin, payload: RpdEntityCreateEventPayload, routerContext?: RouteContext) {
488
- const { modelSingularCode, baseModelSingularCode } = payload;
489
- if (!routerContext) {
490
- routerContext = new RouteContext(this);
491
- }
492
- const entityWatchHandlerContext: EntityWatchHandlerContext<typeof eventName> = {
493
- server: this,
494
- payload,
495
- routerContext,
496
- };
497
-
498
- let emitter: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
499
- if (eventName === "entity.beforeCreate") {
500
- emitter = this.#entityBeforeCreateEventEmitters;
501
- } else if (eventName === "entity.create") {
502
- emitter = this.#entityCreateEventEmitters;
503
- } else if (eventName === "entity.beforeUpdate") {
504
- emitter = this.#entityBeforeUpdateEventEmitters;
505
- } else if (eventName === "entity.update") {
506
- emitter = this.#entityUpdateEventEmitters;
507
- } else if (eventName === "entity.beforeDelete") {
508
- emitter = this.#entityBeforeDeleteEventEmitters;
509
- } else if (eventName === "entity.delete") {
510
- emitter = this.#entityDeleteEventEmitters;
511
- } else if (eventName === "entity.addRelations") {
512
- emitter = this.#entityAddRelationsEventEmitters;
513
- } else if (eventName === "entity.removeRelations") {
514
- emitter = this.#entityRemoveRelationsEventEmitters;
515
- } else if (eventName === "entity.beforeResponse") {
516
- emitter = this.#entityBeforeResponseEventEmitters;
517
- }
518
-
519
- await emitter.emit(modelSingularCode, entityWatchHandlerContext);
520
- if (baseModelSingularCode) {
521
- await emitter.emit(baseModelSingularCode, entityWatchHandlerContext);
522
- }
523
- }
524
- }
1
+ import DataAccessor from "./dataAccess/dataAccessor";
2
+ import {
3
+ GetDataAccessorOptions,
4
+ GetModelOptions,
5
+ IDatabaseAccessor,
6
+ IDatabaseConfig,
7
+ IQueryBuilder,
8
+ IRpdDataAccessor,
9
+ RpdApplicationConfig,
10
+ RpdDataModel,
11
+ RpdServerEventTypes,
12
+ RapidServerConfig,
13
+ RpdDataModelProperty,
14
+ CreateEntityOptions,
15
+ UpdateEntityByIdOptions,
16
+ EntityWatchHandlerContext,
17
+ EntityWatcherType,
18
+ RpdEntityCreateEventPayload,
19
+ EmitServerEventOptions,
20
+ IDatabaseClient,
21
+ } from "./types";
22
+
23
+ import QueryBuilder from "./queryBuilder/queryBuilder";
24
+ import PluginManager from "./core/pluginManager";
25
+ import EventManager from "./core/eventManager";
26
+ import { ActionHandler, ActionHandlerContext, IPluginActionHandler } from "./core/actionHandler";
27
+ import { IRpdServer, RapidPlugin } from "./core/server";
28
+ import { buildRoutes } from "./core/routesBuilder";
29
+ import { Next, RouteContext } from "./core/routeContext";
30
+ import { RapidRequest } from "./core/request";
31
+ import bootstrapApplicationConfig from "./bootstrapApplicationConfig";
32
+ import EntityManager from "./dataAccess/entityManager";
33
+ import { bind, cloneDeep, find, forEach, isString, merge, omit } from "lodash";
34
+ import { Logger } from "./facilities/log/LogFacility";
35
+ import { FacilityFactory } from "./core/facility";
36
+ import { CronJobConfiguration } from "./types/cron-job-types";
37
+
38
+ export interface InitServerOptions {
39
+ logger: Logger;
40
+ databaseAccessor: IDatabaseAccessor;
41
+ databaseConfig: IDatabaseConfig;
42
+ serverConfig: RapidServerConfig;
43
+ applicationConfig?: RpdApplicationConfig;
44
+ facilityFactories?: FacilityFactory[];
45
+ plugins?: RapidPlugin[];
46
+ entityWatchers?: EntityWatcherType[];
47
+
48
+ /**
49
+ * Application level cron jobs.
50
+ */
51
+ cronJobs?: CronJobConfiguration[];
52
+
53
+ /**
54
+ * All cron jobs of the server will be disabled if set `true`.
55
+ */
56
+ disableCronJobs?: boolean;
57
+ }
58
+
59
+ export class RapidServer implements IRpdServer {
60
+ #logger: Logger;
61
+ #facilityFactories: Map<string, FacilityFactory>;
62
+ #pluginManager: PluginManager;
63
+ #plugins: RapidPlugin[];
64
+ #eventManager: EventManager<RpdServerEventTypes>;
65
+ #middlewares: any[];
66
+ #bootstrapApplicationConfig: RpdApplicationConfig;
67
+ #applicationConfig: RpdApplicationConfig;
68
+ #actionHandlersMapByCode: Map<string, ActionHandler>;
69
+ #databaseAccessor: IDatabaseAccessor;
70
+ #cachedDataAccessors: Map<string, DataAccessor>;
71
+
72
+ #entityBeforeCreateEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
73
+ #entityCreateEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
74
+ #entityBeforeUpdateEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
75
+ #entityUpdateEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
76
+ #entityBeforeDeleteEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
77
+ #entityDeleteEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
78
+ #entityAddRelationsEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
79
+ #entityRemoveRelationsEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
80
+ #entityBeforeResponseEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
81
+ #entityWatchers: EntityWatcherType[];
82
+ #appEntityWatchers: EntityWatcherType[];
83
+
84
+ #cronJobs: CronJobConfiguration[];
85
+ #appCronJobs: CronJobConfiguration[];
86
+ #disableCronJobs: boolean;
87
+
88
+ #cachedEntityManager: Map<string, EntityManager>;
89
+ #services: Map<string, any>;
90
+ queryBuilder: IQueryBuilder;
91
+ config: RapidServerConfig;
92
+ databaseConfig: IDatabaseConfig;
93
+ #buildedRoutes: (ctx: any, next: any) => any;
94
+
95
+ constructor(options: InitServerOptions) {
96
+ this.#logger = options.logger;
97
+
98
+ this.#facilityFactories = new Map();
99
+ if (options.facilityFactories) {
100
+ forEach(options.facilityFactories, (factory) => {
101
+ this.registerFacilityFactory(factory);
102
+ });
103
+ }
104
+
105
+ this.#pluginManager = new PluginManager(this);
106
+ this.#eventManager = new EventManager();
107
+ this.#middlewares = [];
108
+ this.#bootstrapApplicationConfig = options.applicationConfig || bootstrapApplicationConfig;
109
+
110
+ this.#applicationConfig = {} as RpdApplicationConfig;
111
+ this.#actionHandlersMapByCode = new Map();
112
+ this.#databaseAccessor = options.databaseAccessor;
113
+ this.#cachedDataAccessors = new Map();
114
+ this.#cachedEntityManager = new Map();
115
+
116
+ this.#entityBeforeCreateEventEmitters = new EventManager();
117
+ this.#entityCreateEventEmitters = new EventManager();
118
+ this.#entityBeforeUpdateEventEmitters = new EventManager();
119
+ this.#entityUpdateEventEmitters = new EventManager();
120
+ this.#entityBeforeDeleteEventEmitters = new EventManager();
121
+ this.#entityDeleteEventEmitters = new EventManager();
122
+ this.#entityAddRelationsEventEmitters = new EventManager();
123
+ this.#entityRemoveRelationsEventEmitters = new EventManager();
124
+ this.#entityBeforeResponseEventEmitters = new EventManager();
125
+
126
+ this.registerEventHandler("entity.beforeCreate", this.#handleEntityEvent.bind(this, "entity.beforeCreate"));
127
+ this.registerEventHandler("entity.create", this.#handleEntityEvent.bind(this, "entity.create"));
128
+ this.registerEventHandler("entity.beforeUpdate", this.#handleEntityEvent.bind(this, "entity.beforeUpdate"));
129
+ this.registerEventHandler("entity.update", this.#handleEntityEvent.bind(this, "entity.update"));
130
+ this.registerEventHandler("entity.beforeDelete", this.#handleEntityEvent.bind(this, "entity.beforeDelete"));
131
+ this.registerEventHandler("entity.delete", this.#handleEntityEvent.bind(this, "entity.delete"));
132
+ this.registerEventHandler("entity.addRelations", this.#handleEntityEvent.bind(this, "entity.addRelations"));
133
+ this.registerEventHandler("entity.removeRelations", this.#handleEntityEvent.bind(this, "entity.removeRelations"));
134
+ this.registerEventHandler("entity.beforeResponse", this.#handleEntityEvent.bind(this, "entity.beforeResponse"));
135
+
136
+ this.#entityWatchers = [];
137
+ this.#appEntityWatchers = options.entityWatchers || [];
138
+
139
+ this.#cronJobs = [];
140
+ this.#appCronJobs = options.cronJobs || [];
141
+ this.#disableCronJobs = !!options.disableCronJobs;
142
+
143
+ this.#services = new Map();
144
+
145
+ this.queryBuilder = new QueryBuilder({
146
+ dbDefaultSchema: options.databaseConfig.dbDefaultSchema,
147
+ });
148
+ this.databaseConfig = options.databaseConfig;
149
+ this.config = options.serverConfig;
150
+
151
+ this.#plugins = options.plugins || [];
152
+ }
153
+
154
+ getLogger(): Logger {
155
+ return this.#logger;
156
+ }
157
+
158
+ getApplicationConfig() {
159
+ return this.#applicationConfig;
160
+ }
161
+
162
+ getDatabaseAccessor(): IDatabaseAccessor {
163
+ return this.#databaseAccessor;
164
+ }
165
+
166
+ appendApplicationConfig(config: Partial<RpdApplicationConfig>) {
167
+ const { models, dataDictionaries, routes } = config;
168
+ if (models) {
169
+ for (const model of models) {
170
+ const originalModel = find(this.#applicationConfig.models, (item) => item.singularCode == model.singularCode);
171
+ if (originalModel) {
172
+ merge(
173
+ originalModel,
174
+ omit(model, ["id", "maintainedBy", "namespace", "singularCode", "pluralCode", "schema", "tableName", "properties", "extensions"]),
175
+ );
176
+ originalModel.name = model.name;
177
+ const originalProperties = originalModel.properties;
178
+ for (const property of model.properties) {
179
+ const originalProperty = find(originalProperties, (item) => item.code == property.code);
180
+ if (originalProperty) {
181
+ originalProperty.name = property.name;
182
+ } else {
183
+ originalProperties.push(property);
184
+ }
185
+ }
186
+ } else {
187
+ this.#applicationConfig.models.push(model);
188
+ }
189
+ }
190
+ }
191
+
192
+ if (dataDictionaries) {
193
+ for (const dataDictionary of dataDictionaries) {
194
+ const originalDataDictionary = find(this.#applicationConfig.dataDictionaries, (item) => item.code == dataDictionary.code);
195
+ if (originalDataDictionary) {
196
+ originalDataDictionary.name = dataDictionary.name;
197
+ originalDataDictionary.description = dataDictionary.description;
198
+ originalDataDictionary.entries = dataDictionary.entries;
199
+ } else {
200
+ this.#applicationConfig.dataDictionaries.push(dataDictionary);
201
+ }
202
+ }
203
+ }
204
+ if (routes) {
205
+ for (const route of routes) {
206
+ const originalRoute = find(this.#applicationConfig.routes, (item) => item.code == route.code);
207
+ if (originalRoute) {
208
+ originalRoute.name = route.name;
209
+ originalRoute.actions = route.actions;
210
+ } else {
211
+ this.#applicationConfig.routes.push(route);
212
+ }
213
+ }
214
+ }
215
+ }
216
+
217
+ appendModelProperties(modelSingularCode: string, properties: RpdDataModelProperty[]) {
218
+ const originalModel = find(this.#applicationConfig.models, (item) => item.singularCode == modelSingularCode);
219
+ if (!originalModel) {
220
+ throw new Error(`Cannot append model properties. Model '${modelSingularCode}' was not found.`);
221
+ }
222
+
223
+ const originalProperties = originalModel.properties;
224
+ for (const property of properties) {
225
+ const originalProperty = find(originalProperties, (item) => item.code == property.code);
226
+ if (originalProperty) {
227
+ originalProperty.name = property.name;
228
+ } else {
229
+ originalProperties.push(property);
230
+ }
231
+ }
232
+ }
233
+
234
+ registerActionHandler(plugin: RapidPlugin, options: IPluginActionHandler) {
235
+ const handler = bind(options.handler, null, plugin);
236
+ this.#actionHandlersMapByCode.set(options.code, handler);
237
+ }
238
+
239
+ getActionHandlerByCode(code: string) {
240
+ return this.#actionHandlersMapByCode.get(code);
241
+ }
242
+
243
+ registerMiddleware(middleware: any) {
244
+ this.#middlewares.push(middleware);
245
+ }
246
+
247
+ getDataAccessor<T = any>(options: GetDataAccessorOptions): IRpdDataAccessor<T> {
248
+ const { namespace, singularCode } = options;
249
+
250
+ let dataAccessor = this.#cachedDataAccessors.get(singularCode);
251
+ if (dataAccessor) {
252
+ return dataAccessor;
253
+ }
254
+
255
+ const model = this.getModel(options);
256
+ if (!model) {
257
+ throw new Error(`Data model ${namespace}.${singularCode} not found.`);
258
+ }
259
+
260
+ dataAccessor = new DataAccessor<T>(this, this.#databaseAccessor, {
261
+ model,
262
+ queryBuilder: this.queryBuilder as QueryBuilder,
263
+ });
264
+ this.#cachedDataAccessors.set(singularCode, dataAccessor);
265
+ return dataAccessor;
266
+ }
267
+
268
+ getModel(options: GetModelOptions): RpdDataModel | undefined {
269
+ if (options.namespace) {
270
+ return this.#applicationConfig?.models.find((e) => e.namespace === options.namespace && e.singularCode === options.singularCode);
271
+ }
272
+
273
+ return this.#applicationConfig?.models.find((e) => e.singularCode === options.singularCode);
274
+ }
275
+
276
+ getEntityManager<TEntity = any>(singularCode: string): EntityManager<TEntity> {
277
+ let entityManager = this.#cachedEntityManager.get(singularCode);
278
+ if (entityManager) {
279
+ return entityManager;
280
+ }
281
+
282
+ const dataAccessor = this.getDataAccessor({ singularCode });
283
+ entityManager = new EntityManager(this, dataAccessor);
284
+ this.#cachedEntityManager.set(singularCode, entityManager);
285
+ return entityManager;
286
+ }
287
+
288
+ registerEventHandler<K extends keyof RpdServerEventTypes>(eventName: K, listener: (...args: RpdServerEventTypes[K]) => void) {
289
+ this.#eventManager.on(eventName, listener);
290
+ }
291
+
292
+ registerEntityWatcher(entityWatcher: EntityWatcherType) {
293
+ this.#entityWatchers.push(entityWatcher);
294
+ }
295
+
296
+ async emitEvent<TEventName extends keyof RpdServerEventTypes>(event: EmitServerEventOptions<TEventName>) {
297
+ const { eventName, payload, sender, routeContext: routerContext } = event;
298
+ this.#logger.debug(`Emitting '${eventName}' event.`, { eventName });
299
+ this.#logger.verbose(`Event payload: `, { payload });
300
+ await this.#eventManager.emit<TEventName>(eventName, sender, payload as any, routerContext);
301
+
302
+ // TODO: should move this logic into metaManager
303
+ // if (
304
+ // (eventName === "entity.create" || eventName === "entity.update" ||
305
+ // eventName === "entity.delete") &&
306
+ // payload.namespace === "meta"
307
+ // ) {
308
+ // await this.configureApplication();
309
+ // }
310
+ }
311
+
312
+ registerService(name: string, service: any) {
313
+ this.#services.set(name, service);
314
+ }
315
+
316
+ getService<TService>(name: string): TService {
317
+ return this.#services.get(name);
318
+ }
319
+
320
+ registerCronJob(job: CronJobConfiguration): void {
321
+ const jobDuplicate = find(this.#cronJobs, (item: CronJobConfiguration) => item.code === job.code);
322
+ if (jobDuplicate) {
323
+ this.#logger.warn(`Duplicated cron job with code "${job.code}"`);
324
+ }
325
+
326
+ this.#cronJobs.push(job);
327
+ }
328
+
329
+ listCronJobs() {
330
+ return [...this.#cronJobs, ...this.#appCronJobs];
331
+ }
332
+
333
+ async start() {
334
+ this.#logger.info("Starting rapid server...");
335
+ const pluginManager = this.#pluginManager;
336
+ await pluginManager.loadPlugins(this.#plugins);
337
+
338
+ await pluginManager.initPlugins();
339
+
340
+ await pluginManager.registerMiddlewares();
341
+ await pluginManager.registerActionHandlers();
342
+ await pluginManager.registerEventHandlers();
343
+ await pluginManager.registerMessageHandlers();
344
+ await pluginManager.registerTaskProcessors();
345
+
346
+ this.#entityWatchers = this.#entityWatchers.concat(this.#appEntityWatchers);
347
+ for (const entityWatcher of this.#entityWatchers) {
348
+ if (entityWatcher.eventName === "entity.beforeCreate") {
349
+ this.#entityBeforeCreateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
350
+ } else if (entityWatcher.eventName === "entity.create") {
351
+ this.#entityCreateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
352
+ } else if (entityWatcher.eventName === "entity.beforeUpdate") {
353
+ this.#entityBeforeUpdateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
354
+ } else if (entityWatcher.eventName === "entity.update") {
355
+ this.#entityUpdateEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
356
+ } else if (entityWatcher.eventName === "entity.beforeDelete") {
357
+ this.#entityBeforeDeleteEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
358
+ } else if (entityWatcher.eventName === "entity.delete") {
359
+ this.#entityDeleteEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
360
+ } else if (entityWatcher.eventName === "entity.addRelations") {
361
+ this.#entityAddRelationsEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
362
+ } else if (entityWatcher.eventName === "entity.removeRelations") {
363
+ this.#entityRemoveRelationsEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
364
+ } else if (entityWatcher.eventName === "entity.beforeResponse") {
365
+ this.#entityBeforeResponseEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
366
+ }
367
+ }
368
+
369
+ await this.configureApplication();
370
+
371
+ if (!this.#disableCronJobs) {
372
+ await pluginManager.registerCronJobs();
373
+ }
374
+
375
+ this.#logger.info(`Rapid server ready.`);
376
+ await pluginManager.onApplicationReady(this.#applicationConfig);
377
+ }
378
+
379
+ async configureApplication() {
380
+ this.#applicationConfig = cloneDeep(this.#bootstrapApplicationConfig) as RpdApplicationConfig;
381
+
382
+ const pluginManager = this.#pluginManager;
383
+ await pluginManager.onLoadingApplication(this.#applicationConfig);
384
+ await pluginManager.configureModels(this.#applicationConfig);
385
+ await pluginManager.configureModelProperties(this.#applicationConfig);
386
+ await pluginManager.configureServices(this.#applicationConfig);
387
+ await pluginManager.configureRoutes(this.#applicationConfig);
388
+
389
+ // TODO: check application configuration.
390
+
391
+ await pluginManager.onApplicationLoaded(this.#applicationConfig);
392
+
393
+ this.#buildedRoutes = await buildRoutes(this, this.#applicationConfig);
394
+ }
395
+
396
+ registerFacilityFactory(factory: FacilityFactory) {
397
+ this.#facilityFactories.set(factory.name, factory);
398
+ }
399
+
400
+ async getFacility<TFacility = any, TOptions = any>(name: string, options?: TOptions, nullIfUnknownFacility?: boolean): Promise<TFacility> {
401
+ const factory = this.#facilityFactories.get(name);
402
+ if (!factory) {
403
+ if (nullIfUnknownFacility) {
404
+ return null;
405
+ } else {
406
+ throw new Error(`Failed to get facility. Unknown facility name: ${name}`);
407
+ }
408
+ }
409
+
410
+ return await factory.createFacility(this, options);
411
+ }
412
+
413
+ async queryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient, dropErrorLog?: boolean): Promise<any[]> {
414
+ try {
415
+ return await this.#databaseAccessor.queryDatabaseObject(sql, params, client);
416
+ } catch (err) {
417
+ if (!dropErrorLog) {
418
+ this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
419
+ }
420
+ throw err;
421
+ }
422
+ }
423
+
424
+ async tryQueryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient, dropErrorLog?: boolean): Promise<any[]> {
425
+ try {
426
+ return await this.queryDatabaseObject(sql, params, client);
427
+ } catch (err) {
428
+ if (!dropErrorLog) {
429
+ this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
430
+ }
431
+ }
432
+
433
+ return [];
434
+ }
435
+
436
+ get middlewares() {
437
+ return this.#middlewares;
438
+ }
439
+
440
+ async handleRequest(rapidRequest: RapidRequest, next: Next) {
441
+ await rapidRequest.parseBody();
442
+ const routeContext: RouteContext = new RouteContext(this, rapidRequest);
443
+ const { response } = routeContext;
444
+
445
+ try {
446
+ await this.#pluginManager.onPrepareRouteContext(routeContext);
447
+ await this.#buildedRoutes(routeContext, next);
448
+ } catch (ex) {
449
+ let error: any;
450
+ if (isString(ex)) {
451
+ error = {
452
+ message: ex,
453
+ };
454
+ } else {
455
+ error = { name: ex.name, message: ex.message, stack: ex.stack };
456
+ }
457
+ this.#logger.error("handle request error.", { error });
458
+ response.json({ error }, 500);
459
+ }
460
+
461
+ if (!response.status && !response.body) {
462
+ response.json(
463
+ {
464
+ error: {
465
+ message: "No route handler was found to handle this request.",
466
+ },
467
+ },
468
+ 404,
469
+ );
470
+ }
471
+ return response.getResponse();
472
+ }
473
+
474
+ async beforeRunRouteActions(handlerContext: ActionHandlerContext) {
475
+ await this.#pluginManager.beforeRunRouteActions(handlerContext);
476
+ }
477
+
478
+ async beforeCreateEntity(model: RpdDataModel, options: CreateEntityOptions) {
479
+ await this.#pluginManager.beforeCreateEntity(model, options);
480
+ }
481
+
482
+ async beforeUpdateEntity(model: RpdDataModel, options: UpdateEntityByIdOptions, currentEntity: any) {
483
+ await this.#pluginManager.beforeUpdateEntity(model, options, currentEntity);
484
+ }
485
+
486
+ async #handleEntityEvent(eventName: keyof RpdServerEventTypes, sender: RapidPlugin, payload: RpdEntityCreateEventPayload, routerContext?: RouteContext) {
487
+ const { modelSingularCode, baseModelSingularCode } = payload;
488
+ if (!routerContext) {
489
+ routerContext = new RouteContext(this);
490
+ }
491
+ const entityWatchHandlerContext: EntityWatchHandlerContext<typeof eventName> = {
492
+ server: this,
493
+ payload,
494
+ routerContext,
495
+ };
496
+
497
+ let emitter: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
498
+ if (eventName === "entity.beforeCreate") {
499
+ emitter = this.#entityBeforeCreateEventEmitters;
500
+ } else if (eventName === "entity.create") {
501
+ emitter = this.#entityCreateEventEmitters;
502
+ } else if (eventName === "entity.beforeUpdate") {
503
+ emitter = this.#entityBeforeUpdateEventEmitters;
504
+ } else if (eventName === "entity.update") {
505
+ emitter = this.#entityUpdateEventEmitters;
506
+ } else if (eventName === "entity.beforeDelete") {
507
+ emitter = this.#entityBeforeDeleteEventEmitters;
508
+ } else if (eventName === "entity.delete") {
509
+ emitter = this.#entityDeleteEventEmitters;
510
+ } else if (eventName === "entity.addRelations") {
511
+ emitter = this.#entityAddRelationsEventEmitters;
512
+ } else if (eventName === "entity.removeRelations") {
513
+ emitter = this.#entityRemoveRelationsEventEmitters;
514
+ } else if (eventName === "entity.beforeResponse") {
515
+ emitter = this.#entityBeforeResponseEventEmitters;
516
+ }
517
+
518
+ await emitter.emit(modelSingularCode, entityWatchHandlerContext);
519
+ if (baseModelSingularCode) {
520
+ await emitter.emit(baseModelSingularCode, entityWatchHandlerContext);
521
+ }
522
+ }
523
+ }