@ruiapp/rapid-core 0.1.51 → 0.1.53

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.
@@ -2,5 +2,5 @@ export default class EventManager<EventTypes extends Record<string, any[]>> {
2
2
  #private;
3
3
  constructor();
4
4
  on<K extends keyof EventTypes>(eventName: K, listener: (...args: EventTypes[K]) => void): void;
5
- emit<K extends keyof EventTypes>(eventName: K, ...args: EventTypes[K]): boolean;
5
+ emit<K extends keyof EventTypes>(eventName: K, ...args: EventTypes[K]): Promise<void>;
6
6
  }
package/dist/index.js CHANGED
@@ -726,8 +726,11 @@ class EventManager {
726
726
  on(eventName, listener) {
727
727
  this.#eventEmitter.on(eventName, listener);
728
728
  }
729
- emit(eventName, ...args) {
730
- return this.#eventEmitter.emit(eventName, ...args);
729
+ async emit(eventName, ...args) {
730
+ const listeners = this.#eventEmitter.listeners(eventName);
731
+ for (const listener of listeners) {
732
+ await listener(...args);
733
+ }
731
734
  }
732
735
  }
733
736
 
@@ -1804,9 +1807,16 @@ function getEntityPropertiesIncludingBase(server, model) {
1804
1807
  const baseModel = server.getModel({
1805
1808
  singularCode: model.base,
1806
1809
  });
1807
- baseModel.properties.forEach(prop => prop.isBaseProperty = true);
1810
+ let baseProperties = [];
1811
+ if (baseModel) {
1812
+ baseModel.properties.map((property) => {
1813
+ property = lodash.cloneDeep(property);
1814
+ property.isBaseProperty = true;
1815
+ return property;
1816
+ });
1817
+ }
1808
1818
  return [
1809
- ...baseModel.properties,
1819
+ ...baseProperties,
1810
1820
  ...model.properties,
1811
1821
  ];
1812
1822
  }
@@ -1823,6 +1833,7 @@ function getEntityProperty(server, model, predicate) {
1823
1833
  });
1824
1834
  property = baseModel.properties.find(predicate);
1825
1835
  if (property) {
1836
+ property = lodash.cloneDeep(property);
1826
1837
  property.isBaseProperty = true;
1827
1838
  }
1828
1839
  }
@@ -2050,11 +2061,11 @@ async function findEntities(server, dataAccessor, options) {
2050
2061
  pagination: options.pagination,
2051
2062
  fields: columnsToSelect,
2052
2063
  };
2053
- const entities = await dataAccessor.find(findRowOptions);
2054
- if (!entities.length) {
2064
+ const rows = await dataAccessor.find(findRowOptions);
2065
+ if (!rows.length) {
2055
2066
  return [];
2056
2067
  }
2057
- const entityIds = entities.map((e) => e.id);
2068
+ const entityIds = rows.map((row) => row.id);
2058
2069
  if (relationPropertiesToSelect.length) {
2059
2070
  for (const relationProperty of relationPropertiesToSelect) {
2060
2071
  const isManyRelation = relationProperty.relation === "many";
@@ -2065,9 +2076,9 @@ async function findEntities(server, dataAccessor, options) {
2065
2076
  }
2066
2077
  if (isManyRelation) {
2067
2078
  const relationLinks = await findManyRelationLinksViaLinkTable(server, targetModel, relationProperty, entityIds);
2068
- lodash.forEach(entities, (entity) => {
2069
- entity[relationProperty.code] = lodash.filter(relationLinks, (link) => {
2070
- return link[relationProperty.selfIdColumnName] == entity["id"];
2079
+ lodash.forEach(rows, (row) => {
2080
+ row[relationProperty.code] = lodash.filter(relationLinks, (link) => {
2081
+ return link[relationProperty.selfIdColumnName] == row["id"];
2071
2082
  }).map((link) => mapDbRowToEntity(server, targetModel, link.targetEntity, false));
2072
2083
  });
2073
2084
  }
@@ -2078,29 +2089,36 @@ async function findEntities(server, dataAccessor, options) {
2078
2089
  relatedEntities = await findManyRelatedEntitiesViaIdPropertyCode(server, model, relationProperty, entityIds);
2079
2090
  }
2080
2091
  else {
2081
- const targetEntityIds = lodash.uniq(lodash.reject(lodash.map(entities, (entity) => entity[relationProperty.targetIdColumnName]), isNullOrUndefined));
2092
+ const targetEntityIds = lodash.uniq(lodash.reject(lodash.map(rows, (entity) => entity[relationProperty.targetIdColumnName]), isNullOrUndefined));
2082
2093
  relatedEntities = await findOneRelatedEntitiesViaIdPropertyCode(server, model, relationProperty, targetEntityIds);
2083
2094
  }
2084
2095
  const targetModel = server.getModel({
2085
2096
  singularCode: relationProperty.targetSingularCode,
2086
2097
  });
2087
- entities.forEach((entity) => {
2098
+ rows.forEach((row) => {
2088
2099
  if (isManyRelation) {
2089
- entity[relationProperty.code] = lodash.filter(relatedEntities, (relatedEntity) => {
2090
- return relatedEntity[relationProperty.selfIdColumnName] == entity.id;
2100
+ row[relationProperty.code] = lodash.filter(relatedEntities, (relatedEntity) => {
2101
+ return relatedEntity[relationProperty.selfIdColumnName] == row.id;
2091
2102
  }).map((item) => mapDbRowToEntity(server, targetModel, item, false));
2092
2103
  }
2093
2104
  else {
2094
- entity[relationProperty.code] = mapDbRowToEntity(server, targetModel, lodash.find(relatedEntities, (relatedEntity) => {
2105
+ row[relationProperty.code] = mapDbRowToEntity(server, targetModel, lodash.find(relatedEntities, (relatedEntity) => {
2095
2106
  // TODO: id property code should be configurable.
2096
- return relatedEntity["id"] == entity[relationProperty.targetIdColumnName];
2107
+ return relatedEntity["id"] == row[relationProperty.targetIdColumnName];
2097
2108
  }), false);
2098
2109
  }
2099
2110
  });
2100
2111
  }
2101
2112
  }
2102
2113
  }
2103
- return entities.map((item) => mapDbRowToEntity(server, model, item, options.keepNonPropertyFields));
2114
+ const entities = rows.map((item) => mapDbRowToEntity(server, model, item, options.keepNonPropertyFields));
2115
+ await server.emitEvent("entity.beforeResponse", {
2116
+ namespace: model.namespace,
2117
+ modelSingularCode: model.singularCode,
2118
+ baseModelSingularCode: model.base,
2119
+ entities,
2120
+ }, null);
2121
+ return entities;
2104
2122
  }
2105
2123
  async function findEntity(server, dataAccessor, options) {
2106
2124
  const entities = await findEntities(server, dataAccessor, options);
@@ -2867,6 +2885,7 @@ class RapidServer {
2867
2885
  #entityDeleteEventEmitters;
2868
2886
  #entityAddRelationsEventEmitters;
2869
2887
  #entityRemoveRelationsEventEmitters;
2888
+ #entityBeforeResponseEventEmitters;
2870
2889
  #entityWatchers;
2871
2890
  #appEntityWatchers;
2872
2891
  #cachedEntityManager;
@@ -2900,6 +2919,7 @@ class RapidServer {
2900
2919
  this.#entityDeleteEventEmitters = new EventManager();
2901
2920
  this.#entityAddRelationsEventEmitters = new EventManager();
2902
2921
  this.#entityRemoveRelationsEventEmitters = new EventManager();
2922
+ this.#entityBeforeResponseEventEmitters = new EventManager();
2903
2923
  this.registerEventHandler("entity.beforeCreate", this.#handleEntityEvent.bind(this, "entity.beforeCreate"));
2904
2924
  this.registerEventHandler("entity.create", this.#handleEntityEvent.bind(this, "entity.create"));
2905
2925
  this.registerEventHandler("entity.beforeUpdate", this.#handleEntityEvent.bind(this, "entity.beforeUpdate"));
@@ -2908,6 +2928,7 @@ class RapidServer {
2908
2928
  this.registerEventHandler("entity.delete", this.#handleEntityEvent.bind(this, "entity.delete"));
2909
2929
  this.registerEventHandler("entity.addRelations", this.#handleEntityEvent.bind(this, "entity.addRelations"));
2910
2930
  this.registerEventHandler("entity.removeRelations", this.#handleEntityEvent.bind(this, "entity.removeRelations"));
2931
+ this.registerEventHandler("entity.beforeResponse", this.#handleEntityEvent.bind(this, "entity.beforeResponse"));
2911
2932
  this.#entityWatchers = [];
2912
2933
  this.#appEntityWatchers = options.entityWatchers || [];
2913
2934
  this.#services = new Map();
@@ -3080,6 +3101,9 @@ class RapidServer {
3080
3101
  else if (entityWatcher.eventName === "entity.removeRelations") {
3081
3102
  this.#entityRemoveRelationsEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
3082
3103
  }
3104
+ else if (entityWatcher.eventName === "entity.beforeResponse") {
3105
+ this.#entityBeforeResponseEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
3106
+ }
3083
3107
  }
3084
3108
  await this.configureApplication();
3085
3109
  this.#logger.info(`Rapid server ready.`);
@@ -3160,7 +3184,7 @@ class RapidServer {
3160
3184
  async beforeUpdateEntity(model, options, currentEntity) {
3161
3185
  await this.#pluginManager.beforeUpdateEntity(model, options, currentEntity);
3162
3186
  }
3163
- #handleEntityEvent(eventName, sender, payload) {
3187
+ async #handleEntityEvent(eventName, sender, payload) {
3164
3188
  const { modelSingularCode, baseModelSingularCode } = payload;
3165
3189
  const entityWatchHandlerContext = {
3166
3190
  server: this,
@@ -3191,9 +3215,12 @@ class RapidServer {
3191
3215
  else if (eventName === "entity.removeRelations") {
3192
3216
  emitter = this.#entityRemoveRelationsEventEmitters;
3193
3217
  }
3194
- emitter.emit(modelSingularCode, entityWatchHandlerContext);
3218
+ else if (eventName === "entity.beforeResponse") {
3219
+ emitter = this.#entityBeforeResponseEventEmitters;
3220
+ }
3221
+ await emitter.emit(modelSingularCode, entityWatchHandlerContext);
3195
3222
  if (baseModelSingularCode) {
3196
- emitter.emit(baseModelSingularCode, entityWatchHandlerContext);
3223
+ await emitter.emit(baseModelSingularCode, entityWatchHandlerContext);
3197
3224
  }
3198
3225
  }
3199
3226
  }
package/dist/types.d.ts CHANGED
@@ -63,6 +63,7 @@ export type RpdServerEventTypes = {
63
63
  "entity.delete": [RapidPlugin, RpdEntityDeleteEventPayload];
64
64
  "entity.addRelations": [RapidPlugin, RpdEntityAddRelationsEventPayload];
65
65
  "entity.removeRelations": [RapidPlugin, RpdEntityRemoveRelationsEventPayload];
66
+ "entity.beforeResponse": [RapidPlugin, RpdEntityBeforeResponseEventPayload];
66
67
  };
67
68
  export interface RpdEntityBeforeCreateEventPayload {
68
69
  namespace: string;
@@ -119,6 +120,12 @@ export interface RpdEntityRemoveRelationsEventPayload {
119
120
  property: string;
120
121
  relations: any[];
121
122
  }
123
+ export interface RpdEntityBeforeResponseEventPayload {
124
+ namespace: string;
125
+ modelSingularCode: string;
126
+ baseModelSingularCode?: string;
127
+ entities: any[];
128
+ }
122
129
  export interface QuoteTableOptions {
123
130
  schema?: string;
124
131
  tableName: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruiapp/rapid-core",
3
- "version": "0.1.51",
3
+ "version": "0.1.53",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,7 +11,10 @@ export default class EventManager<EventTypes extends Record<string, any[]>> {
11
11
  this.#eventEmitter.on(eventName as string, listener);
12
12
  }
13
13
 
14
- emit<K extends keyof EventTypes>(eventName: K, ...args: EventTypes[K]) {
15
- return this.#eventEmitter.emit(eventName as string, ...args);
14
+ async emit<K extends keyof EventTypes>(eventName: K, ...args: EventTypes[K]) {
15
+ const listeners = this.#eventEmitter.listeners(eventName as string);
16
+ for (const listener of listeners) {
17
+ await listener(...args);
18
+ }
16
19
  }
17
20
  }
@@ -129,12 +129,12 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
129
129
  pagination: options.pagination,
130
130
  fields: columnsToSelect,
131
131
  };
132
- const entities = await dataAccessor.find(findRowOptions);
133
- if (!entities.length) {
132
+ const rows = await dataAccessor.find(findRowOptions);
133
+ if (!rows.length) {
134
134
  return [];
135
135
  }
136
136
 
137
- const entityIds = entities.map((e) => e.id);
137
+ const entityIds = rows.map((row) => row.id);
138
138
  if (relationPropertiesToSelect.length) {
139
139
  for (const relationProperty of relationPropertiesToSelect) {
140
140
  const isManyRelation = relationProperty.relation === "many";
@@ -148,9 +148,9 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
148
148
  if (isManyRelation) {
149
149
  const relationLinks = await findManyRelationLinksViaLinkTable(server, targetModel, relationProperty, entityIds);
150
150
 
151
- forEach(entities, (entity: any) => {
152
- entity[relationProperty.code] = filter(relationLinks, (link: any) => {
153
- return link[relationProperty.selfIdColumnName!] == entity["id"];
151
+ forEach(rows, (row: any) => {
152
+ row[relationProperty.code] = filter(relationLinks, (link: any) => {
153
+ return link[relationProperty.selfIdColumnName!] == row["id"];
154
154
  }).map((link) => mapDbRowToEntity(server, targetModel, link.targetEntity, false));
155
155
  });
156
156
  }
@@ -161,7 +161,7 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
161
161
  } else {
162
162
  const targetEntityIds = uniq(
163
163
  reject(
164
- map(entities, (entity: any) => entity[relationProperty.targetIdColumnName!]),
164
+ map(rows, (entity: any) => entity[relationProperty.targetIdColumnName!]),
165
165
  isNullOrUndefined,
166
166
  ),
167
167
  );
@@ -171,18 +171,18 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
171
171
  const targetModel = server.getModel({
172
172
  singularCode: relationProperty.targetSingularCode!,
173
173
  });
174
- entities.forEach((entity) => {
174
+ rows.forEach((row) => {
175
175
  if (isManyRelation) {
176
- entity[relationProperty.code] = filter(relatedEntities, (relatedEntity: any) => {
177
- return relatedEntity[relationProperty.selfIdColumnName!] == entity.id;
176
+ row[relationProperty.code] = filter(relatedEntities, (relatedEntity: any) => {
177
+ return relatedEntity[relationProperty.selfIdColumnName!] == row.id;
178
178
  }).map((item) => mapDbRowToEntity(server, targetModel!, item, false));
179
179
  } else {
180
- entity[relationProperty.code] = mapDbRowToEntity(
180
+ row[relationProperty.code] = mapDbRowToEntity(
181
181
  server,
182
182
  targetModel!,
183
183
  find(relatedEntities, (relatedEntity: any) => {
184
184
  // TODO: id property code should be configurable.
185
- return relatedEntity["id"] == entity[relationProperty.targetIdColumnName!];
185
+ return relatedEntity["id"] == row[relationProperty.targetIdColumnName!];
186
186
  }),
187
187
  false,
188
188
  );
@@ -191,7 +191,20 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
191
191
  }
192
192
  }
193
193
  }
194
- return entities.map((item) => mapDbRowToEntity(server, model, item, options.keepNonPropertyFields));
194
+ const entities = rows.map((item) => mapDbRowToEntity(server, model, item, options.keepNonPropertyFields));
195
+
196
+ await server.emitEvent(
197
+ "entity.beforeResponse",
198
+ {
199
+ namespace: model.namespace,
200
+ modelSingularCode: model.singularCode,
201
+ baseModelSingularCode: model.base,
202
+ entities,
203
+ },
204
+ null,
205
+ );
206
+
207
+ return entities;
195
208
  }
196
209
 
197
210
  async function findEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor, options: FindEntityOptions) {
@@ -1,3 +1,4 @@
1
+ import { cloneDeep } from "lodash";
1
2
  import { IRpdServer } from "~/core/server";
2
3
  import { RpdDataModel, RpdDataModelProperty } from "~/types";
3
4
 
@@ -13,10 +14,17 @@ export function getEntityPropertiesIncludingBase(server: IRpdServer, model: RpdD
13
14
  const baseModel = server.getModel({
14
15
  singularCode: model.base,
15
16
  });
16
- baseModel.properties.forEach(prop => prop.isBaseProperty = true);
17
+ let baseProperties: RpdDataModelProperty[] = [];
18
+ if (baseModel) {
19
+ baseModel.properties.map((property) => {
20
+ property = cloneDeep(property);
21
+ property.isBaseProperty = true;
22
+ return property;
23
+ })
24
+ }
17
25
 
18
26
  return [
19
- ...baseModel.properties,
27
+ ...baseProperties,
20
28
  ...model.properties,
21
29
  ]
22
30
  }
@@ -36,6 +44,7 @@ export function getEntityProperty(server: IRpdServer, model: RpdDataModel, predi
36
44
 
37
45
  property = baseModel.properties.find(predicate);
38
46
  if (property) {
47
+ property = cloneDeep(property);
39
48
  property.isBaseProperty = true;
40
49
  }
41
50
  }
package/src/server.ts CHANGED
@@ -47,6 +47,7 @@ export class RapidServer implements IRpdServer {
47
47
  #entityDeleteEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
48
48
  #entityAddRelationsEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
49
49
  #entityRemoveRelationsEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
50
+ #entityBeforeResponseEventEmitters: EventManager<Record<string, [EntityWatchHandlerContext<any>]>>;
50
51
  #entityWatchers: EntityWatcherType[];
51
52
  #appEntityWatchers: EntityWatcherType[];
52
53
 
@@ -87,6 +88,7 @@ export class RapidServer implements IRpdServer {
87
88
  this.#entityDeleteEventEmitters = new EventManager();
88
89
  this.#entityAddRelationsEventEmitters = new EventManager();
89
90
  this.#entityRemoveRelationsEventEmitters = new EventManager();
91
+ this.#entityBeforeResponseEventEmitters = new EventManager();
90
92
 
91
93
  this.registerEventHandler("entity.beforeCreate", this.#handleEntityEvent.bind(this, "entity.beforeCreate"));
92
94
  this.registerEventHandler("entity.create", this.#handleEntityEvent.bind(this, "entity.create"));
@@ -96,6 +98,7 @@ export class RapidServer implements IRpdServer {
96
98
  this.registerEventHandler("entity.delete", this.#handleEntityEvent.bind(this, "entity.delete"));
97
99
  this.registerEventHandler("entity.addRelations", this.#handleEntityEvent.bind(this, "entity.addRelations"));
98
100
  this.registerEventHandler("entity.removeRelations", this.#handleEntityEvent.bind(this, "entity.removeRelations"));
101
+ this.registerEventHandler("entity.beforeResponse", this.#handleEntityEvent.bind(this, "entity.beforeResponse"));
99
102
 
100
103
  this.#entityWatchers = [];
101
104
  this.#appEntityWatchers = options.entityWatchers || [];
@@ -287,6 +290,8 @@ export class RapidServer implements IRpdServer {
287
290
  this.#entityAddRelationsEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
288
291
  } else if (entityWatcher.eventName === "entity.removeRelations") {
289
292
  this.#entityRemoveRelationsEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
293
+ } else if (entityWatcher.eventName === "entity.beforeResponse") {
294
+ this.#entityBeforeResponseEventEmitters.on(entityWatcher.modelSingularCode, entityWatcher.handler);
290
295
  }
291
296
  }
292
297
 
@@ -387,7 +392,7 @@ export class RapidServer implements IRpdServer {
387
392
  await this.#pluginManager.beforeUpdateEntity(model, options, currentEntity);
388
393
  }
389
394
 
390
- #handleEntityEvent(eventName: keyof RpdServerEventTypes, sender: RapidPlugin, payload: RpdEntityCreateEventPayload) {
395
+ async #handleEntityEvent(eventName: keyof RpdServerEventTypes, sender: RapidPlugin, payload: RpdEntityCreateEventPayload) {
391
396
  const { modelSingularCode, baseModelSingularCode } = payload;
392
397
  const entityWatchHandlerContext: EntityWatchHandlerContext<typeof eventName> = {
393
398
  server: this,
@@ -411,11 +416,13 @@ export class RapidServer implements IRpdServer {
411
416
  emitter = this.#entityAddRelationsEventEmitters;
412
417
  } else if (eventName === "entity.removeRelations") {
413
418
  emitter = this.#entityRemoveRelationsEventEmitters;
419
+ } else if (eventName === "entity.beforeResponse") {
420
+ emitter = this.#entityBeforeResponseEventEmitters;
414
421
  }
415
422
 
416
- emitter.emit(modelSingularCode, entityWatchHandlerContext);
423
+ await emitter.emit(modelSingularCode, entityWatchHandlerContext);
417
424
  if (baseModelSingularCode) {
418
- emitter.emit(baseModelSingularCode, entityWatchHandlerContext);
425
+ await emitter.emit(baseModelSingularCode, entityWatchHandlerContext);
419
426
  }
420
427
  }
421
428
  }
package/src/types.ts CHANGED
@@ -75,6 +75,7 @@ export type RpdServerEventTypes = {
75
75
  "entity.delete": [RapidPlugin, RpdEntityDeleteEventPayload];
76
76
  "entity.addRelations": [RapidPlugin, RpdEntityAddRelationsEventPayload];
77
77
  "entity.removeRelations": [RapidPlugin, RpdEntityRemoveRelationsEventPayload];
78
+ "entity.beforeResponse": [RapidPlugin, RpdEntityBeforeResponseEventPayload];
78
79
  };
79
80
 
80
81
  export interface RpdEntityBeforeCreateEventPayload {
@@ -140,6 +141,13 @@ export interface RpdEntityRemoveRelationsEventPayload {
140
141
  relations: any[];
141
142
  }
142
143
 
144
+ export interface RpdEntityBeforeResponseEventPayload {
145
+ namespace: string;
146
+ modelSingularCode: string;
147
+ baseModelSingularCode?: string;
148
+ entities: any[];
149
+ }
150
+
143
151
  export interface QuoteTableOptions {
144
152
  schema?: string;
145
153
  tableName: string;