@magek/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/dist/authorizer.d.ts +7 -0
  2. package/dist/authorizer.js +35 -0
  3. package/dist/command-dispatcher.d.ts +8 -0
  4. package/dist/command-dispatcher.js +55 -0
  5. package/dist/core-concepts/data-migration/entities/data-migration-entity.d.ts +12 -0
  6. package/dist/core-concepts/data-migration/entities/data-migration-entity.js +37 -0
  7. package/dist/core-concepts/data-migration/events/data-migration-finished.d.ts +7 -0
  8. package/dist/core-concepts/data-migration/events/data-migration-finished.js +13 -0
  9. package/dist/core-concepts/data-migration/events/data-migration-started.d.ts +7 -0
  10. package/dist/core-concepts/data-migration/events/data-migration-started.js +13 -0
  11. package/dist/core-concepts/data-migration/events/entity-migrated.d.ts +9 -0
  12. package/dist/core-concepts/data-migration/events/entity-migrated.js +15 -0
  13. package/dist/core-concepts/touch-entity/events/entity-touched.d.ts +7 -0
  14. package/dist/core-concepts/touch-entity/events/entity-touched.js +13 -0
  15. package/dist/data-migrations.d.ts +8 -0
  16. package/dist/data-migrations.js +73 -0
  17. package/dist/decorators/command.d.ts +19 -0
  18. package/dist/decorators/command.js +47 -0
  19. package/dist/decorators/data-migration.d.ts +9 -0
  20. package/dist/decorators/data-migration.js +25 -0
  21. package/dist/decorators/entity.d.ts +32 -0
  22. package/dist/decorators/entity.js +100 -0
  23. package/dist/decorators/event-handler.d.ts +3 -0
  24. package/dist/decorators/event-handler.js +18 -0
  25. package/dist/decorators/event.d.ts +8 -0
  26. package/dist/decorators/event.js +22 -0
  27. package/dist/decorators/field-metadata-reader.d.ts +6 -0
  28. package/dist/decorators/field-metadata-reader.js +221 -0
  29. package/dist/decorators/global-error-handler.d.ts +2 -0
  30. package/dist/decorators/global-error-handler.js +15 -0
  31. package/dist/decorators/global-event-handler.d.ts +3 -0
  32. package/dist/decorators/global-event-handler.js +9 -0
  33. package/dist/decorators/health-sensor.d.ts +14 -0
  34. package/dist/decorators/health-sensor.js +38 -0
  35. package/dist/decorators/index.d.ts +16 -0
  36. package/dist/decorators/index.js +19 -0
  37. package/dist/decorators/metadata.d.ts +13 -0
  38. package/dist/decorators/metadata.js +55 -0
  39. package/dist/decorators/non-exposed.d.ts +2 -0
  40. package/dist/decorators/non-exposed.js +24 -0
  41. package/dist/decorators/notification.d.ts +35 -0
  42. package/dist/decorators/notification.js +94 -0
  43. package/dist/decorators/projects.d.ts +32 -0
  44. package/dist/decorators/projects.js +87 -0
  45. package/dist/decorators/query.d.ts +2 -0
  46. package/dist/decorators/query.js +25 -0
  47. package/dist/decorators/read-model.d.ts +39 -0
  48. package/dist/decorators/read-model.js +129 -0
  49. package/dist/decorators/role.d.ts +6 -0
  50. package/dist/decorators/role.js +15 -0
  51. package/dist/decorators/scheduled-command.d.ts +9 -0
  52. package/dist/decorators/scheduled-command.js +25 -0
  53. package/dist/decorators/schema-migration.d.ts +36 -0
  54. package/dist/decorators/schema-migration.js +146 -0
  55. package/dist/decorators/sequenced-by.d.ts +28 -0
  56. package/dist/decorators/sequenced-by.js +79 -0
  57. package/dist/decorators/stage3-utils.d.ts +6 -0
  58. package/dist/decorators/stage3-utils.js +25 -0
  59. package/dist/delete-event-dispatcher.d.ts +4 -0
  60. package/dist/delete-event-dispatcher.js +23 -0
  61. package/dist/event-dispatcher.d.ts +9 -0
  62. package/dist/event-dispatcher.js +37 -0
  63. package/dist/event-processor.d.ts +15 -0
  64. package/dist/event-processor.js +125 -0
  65. package/dist/event-search.d.ts +2 -0
  66. package/dist/event-search.js +26 -0
  67. package/dist/event-stream-consumer.d.ts +7 -0
  68. package/dist/event-stream-consumer.js +36 -0
  69. package/dist/event-stream-producer.d.ts +7 -0
  70. package/dist/event-stream-producer.js +30 -0
  71. package/dist/events-reader.d.ts +11 -0
  72. package/dist/events-reader.js +63 -0
  73. package/dist/global-error-dispatcher.d.ts +16 -0
  74. package/dist/global-error-dispatcher.js +109 -0
  75. package/dist/graphql-dispatcher.d.ts +16 -0
  76. package/dist/graphql-dispatcher.js +195 -0
  77. package/dist/importer.d.ts +14 -0
  78. package/dist/importer.js +49 -0
  79. package/dist/index.d.ts +60 -0
  80. package/dist/index.js +100 -0
  81. package/dist/injectable/index.d.ts +21 -0
  82. package/dist/injectable/index.js +2 -0
  83. package/dist/instrumentation/decorator/trace.d.ts +13 -0
  84. package/dist/instrumentation/decorator/trace.js +116 -0
  85. package/dist/instrumentation/index.d.ts +2 -0
  86. package/dist/instrumentation/index.js +5 -0
  87. package/dist/instrumentation/trace-notifier.d.ts +3 -0
  88. package/dist/instrumentation/trace-notifier.js +26 -0
  89. package/dist/magek.d.ts +42 -0
  90. package/dist/magek.js +158 -0
  91. package/dist/query-dispatcher.d.ts +8 -0
  92. package/dist/query-dispatcher.js +47 -0
  93. package/dist/read-model-schema-migrator.d.ts +14 -0
  94. package/dist/read-model-schema-migrator.js +80 -0
  95. package/dist/read-models-reader.d.ts +31 -0
  96. package/dist/read-models-reader.js +196 -0
  97. package/dist/register-handler.d.ts +11 -0
  98. package/dist/register-handler.js +95 -0
  99. package/dist/rocket-dispatcher.d.ts +6 -0
  100. package/dist/rocket-dispatcher.js +21 -0
  101. package/dist/scheduled-command-dispatcher.d.ts +12 -0
  102. package/dist/scheduled-command-dispatcher.js +54 -0
  103. package/dist/schema-migrator.d.ts +12 -0
  104. package/dist/schema-migrator.js +71 -0
  105. package/dist/sensor/health/health-indicators/database-events-health-indicator.d.ts +5 -0
  106. package/dist/sensor/health/health-indicators/database-events-health-indicator.js +26 -0
  107. package/dist/sensor/health/health-indicators/database-health-indicator.d.ts +5 -0
  108. package/dist/sensor/health/health-indicators/database-health-indicator.js +29 -0
  109. package/dist/sensor/health/health-indicators/database-read-models-health-indicator.d.ts +5 -0
  110. package/dist/sensor/health/health-indicators/database-read-models-health-indicator.js +26 -0
  111. package/dist/sensor/health/health-indicators/default-health-indicators.d.ts +5 -0
  112. package/dist/sensor/health/health-indicators/default-health-indicators.js +39 -0
  113. package/dist/sensor/health/health-indicators/function-health-indicator.d.ts +5 -0
  114. package/dist/sensor/health/health-indicators/function-health-indicator.js +30 -0
  115. package/dist/sensor/health/health-indicators/health-indicator.d.ts +5 -0
  116. package/dist/sensor/health/health-indicators/health-indicator.js +30 -0
  117. package/dist/sensor/health/health-indicators/index.d.ts +3 -0
  118. package/dist/sensor/health/health-indicators/index.js +6 -0
  119. package/dist/sensor/health/health-indicators/os-info.d.ts +14 -0
  120. package/dist/sensor/health/health-indicators/os-info.js +38 -0
  121. package/dist/sensor/health/health-indicators/rockets-health-indicator.d.ts +5 -0
  122. package/dist/sensor/health/health-indicators/rockets-health-indicator.js +57 -0
  123. package/dist/sensor/health/health-indicators/version.d.ts +2 -0
  124. package/dist/sensor/health/health-indicators/version.js +24 -0
  125. package/dist/sensor/health/health-service.d.ts +22 -0
  126. package/dist/sensor/health/health-service.js +117 -0
  127. package/dist/sensor/health/health-utils.d.ts +7 -0
  128. package/dist/sensor/health/health-utils.js +53 -0
  129. package/dist/sensor/health/index.d.ts +3 -0
  130. package/dist/sensor/health/index.js +6 -0
  131. package/dist/sensor/index.d.ts +1 -0
  132. package/dist/sensor/index.js +4 -0
  133. package/dist/services/event-store.d.ts +27 -0
  134. package/dist/services/event-store.js +260 -0
  135. package/dist/services/filter-helpers.d.ts +3 -0
  136. package/dist/services/filter-helpers.js +19 -0
  137. package/dist/services/graphql/common.d.ts +26 -0
  138. package/dist/services/graphql/common.js +53 -0
  139. package/dist/services/graphql/graphql-generator.d.ts +46 -0
  140. package/dist/services/graphql/graphql-generator.js +269 -0
  141. package/dist/services/graphql/graphql-mutation-generator.d.ts +12 -0
  142. package/dist/services/graphql/graphql-mutation-generator.js +25 -0
  143. package/dist/services/graphql/graphql-query-generator.d.ts +22 -0
  144. package/dist/services/graphql/graphql-query-generator.js +39 -0
  145. package/dist/services/graphql/graphql-subcriptions-generator.d.ts +17 -0
  146. package/dist/services/graphql/graphql-subcriptions-generator.js +60 -0
  147. package/dist/services/graphql/graphql-type-informer.d.ts +23 -0
  148. package/dist/services/graphql/graphql-type-informer.js +160 -0
  149. package/dist/services/graphql/query-generators/graphql-query-by-keys-generator.d.ts +14 -0
  150. package/dist/services/graphql/query-generators/graphql-query-by-keys-generator.js +48 -0
  151. package/dist/services/graphql/query-generators/graphql-query-events-generator.d.ts +11 -0
  152. package/dist/services/graphql/query-generators/graphql-query-events-generator.js +68 -0
  153. package/dist/services/graphql/query-generators/graphql-query-filters-generator.d.ts +14 -0
  154. package/dist/services/graphql/query-generators/graphql-query-filters-generator.js +31 -0
  155. package/dist/services/graphql/query-generators/graphql-query-generator.d.ts +12 -0
  156. package/dist/services/graphql/query-generators/graphql-query-generator.js +17 -0
  157. package/dist/services/graphql/query-generators/graphql-query-listed-generator.d.ts +16 -0
  158. package/dist/services/graphql/query-generators/graphql-query-listed-generator.js +65 -0
  159. package/dist/services/graphql/query-helpers/graphql-handled-fields-generator.d.ts +15 -0
  160. package/dist/services/graphql/query-helpers/graphql-handled-fields-generator.js +65 -0
  161. package/dist/services/graphql/query-helpers/graphql-query-filter-arguments-builder.d.ts +13 -0
  162. package/dist/services/graphql/query-helpers/graphql-query-filter-arguments-builder.js +169 -0
  163. package/dist/services/graphql/query-helpers/graphql-query-filter-fields-builder.d.ts +11 -0
  164. package/dist/services/graphql/query-helpers/graphql-query-filter-fields-builder.js +28 -0
  165. package/dist/services/graphql/query-helpers/graphql-query-sort-builder.d.ts +12 -0
  166. package/dist/services/graphql/query-helpers/graphql-query-sort-builder.js +61 -0
  167. package/dist/services/graphql/websocket-protocol/graphql-websocket-protocol.d.ts +20 -0
  168. package/dist/services/graphql/websocket-protocol/graphql-websocket-protocol.js +127 -0
  169. package/dist/services/pub-sub/noop-read-model-pub-sub.d.ts +5 -0
  170. package/dist/services/pub-sub/noop-read-model-pub-sub.js +10 -0
  171. package/dist/services/pub-sub/read-model-pub-sub.d.ts +9 -0
  172. package/dist/services/pub-sub/read-model-pub-sub.js +112 -0
  173. package/dist/services/raw-events-parser.d.ts +5 -0
  174. package/dist/services/raw-events-parser.js +44 -0
  175. package/dist/services/read-model-searcher.d.ts +2 -0
  176. package/dist/services/read-model-searcher.js +11 -0
  177. package/dist/services/read-model-store.d.ts +41 -0
  178. package/dist/services/read-model-store.js +295 -0
  179. package/dist/services/token-verifiers/index.d.ts +4 -0
  180. package/dist/services/token-verifiers/index.js +7 -0
  181. package/dist/services/token-verifiers/jwks-uri-token-verifier.d.ts +21 -0
  182. package/dist/services/token-verifiers/jwks-uri-token-verifier.js +23 -0
  183. package/dist/services/token-verifiers/public-key-token-verifier.d.ts +13 -0
  184. package/dist/services/token-verifiers/public-key-token-verifier.js +19 -0
  185. package/dist/services/token-verifiers/role-based-token-verifier.d.ts +8 -0
  186. package/dist/services/token-verifiers/role-based-token-verifier.js +35 -0
  187. package/dist/services/token-verifiers/utilities.d.ts +31 -0
  188. package/dist/services/token-verifiers/utilities.js +70 -0
  189. package/dist/subscribers-notifier.d.ts +14 -0
  190. package/dist/subscribers-notifier.js +109 -0
  191. package/dist/token-verifier.d.ts +11 -0
  192. package/dist/token-verifier.js +46 -0
  193. package/dist/touch-entity-handler.d.ts +4 -0
  194. package/dist/touch-entity-handler.js +16 -0
  195. package/package.json +71 -0
@@ -0,0 +1,295 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReadModelStore = void 0;
4
+ const common_1 = require("@magek/common");
5
+ const global_error_dispatcher_1 = require("../global-error-dispatcher");
6
+ const read_model_searcher_1 = require("./read-model-searcher");
7
+ const read_model_schema_migrator_1 = require("../read-model-schema-migrator");
8
+ class ReadModelStore {
9
+ constructor(config) {
10
+ this.config = config;
11
+ }
12
+ async project(entitySnapshotEnvelope, deleteEvent = false) {
13
+ const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#project');
14
+ const projections = deleteEvent
15
+ ? this.getUnProjections(entitySnapshotEnvelope)
16
+ : this.entityProjections(entitySnapshotEnvelope);
17
+ if (!projections) {
18
+ logger.debug(`No projections found for entity ${entitySnapshotEnvelope.entityTypeName}. Skipping...`);
19
+ return;
20
+ }
21
+ logger.debug(`Projections found for entity ${entitySnapshotEnvelope.entityTypeName}: ${JSON.stringify(projections)}`);
22
+ const entityMetadata = this.config.entities[entitySnapshotEnvelope.entityTypeName];
23
+ const entityInstance = (0, common_1.createInstance)(entityMetadata.class, entitySnapshotEnvelope.value);
24
+ const projectReadModelPromises = projections.flatMap((projectionMetadata) => {
25
+ logger.debug(`Projecting entity snapshot ${entitySnapshotEnvelope} to build new state of read model with projectionMetadata ${JSON.stringify(projectionMetadata)}`);
26
+ const readModelName = projectionMetadata.class.name;
27
+ const sequenceKey = this.sequenceKeyForProjection(entityInstance, projectionMetadata);
28
+ return this.projectEntity(entityInstance, projectionMetadata, entityMetadata, entitySnapshotEnvelope, readModelName, deleteEvent, sequenceKey);
29
+ });
30
+ await common_1.Promises.allSettledAndFulfilled(projectReadModelPromises);
31
+ }
32
+ /**
33
+ * Gets the read models for a given entity instance using the projection metadata
34
+ * @param {EntityInterface} entityInstance The entity instance to get the read models for
35
+ * @param {ProjectionMetadata<EntityInterface, ReadModelInterface>} projectionMetadata The projection metadata to use to get the read models
36
+ * @param {EntityMetadata} entityMetadata The entity metadata for the entity instance
37
+ * @returns {Promise<Array<ReadModelInterface>>} The read models for the entity instance
38
+ */
39
+ async getReadModels(entityInstance, projectionMetadata, entityMetadata) {
40
+ const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#getReadModels');
41
+ logger.debug(`Looking for ReadModels for entity ${JSON.stringify(entityInstance)} using Filter ${projectionMetadata.joinKey}`);
42
+ const readModelName = projectionMetadata.class.name;
43
+ const readModelMetadata = this.config.readModels[readModelName];
44
+ const filter = this.filterForProjection(entityInstance, projectionMetadata, entityMetadata);
45
+ if (!filter) {
46
+ return [];
47
+ }
48
+ logger.debug(`Calling ReadModelSearcher searching for ReadModels for entity ${readModelMetadata.class.name} using Filter ${filter}`);
49
+ const rawReadModels = (await (0, read_model_searcher_1.readModelSearcher)(this.config, readModelMetadata.class)
50
+ .filter(filter)
51
+ .paginatedVersion(false)
52
+ .search());
53
+ return this.instanceReadModels(readModelName, rawReadModels);
54
+ }
55
+ /**
56
+ * Gets a specific read model instance referencing it by ID when it's a regular read model
57
+ * or by ID + sequenceKey when it's a sequenced read model
58
+ * @param {string} readModelName The name of the read model class
59
+ * @param {UUID | undefined} readModelID The ID of the read model instance
60
+ * @param {SequenceKey} sequenceKey The sequence key of the read model instance
61
+ * @returns {Promise<ReadModelInterface | undefined>} The read model instance or undefined if it doesn't exist
62
+ */
63
+ async fetchReadModel(readModelName, readModelID, sequenceKey) {
64
+ if (!readModelID) {
65
+ return undefined;
66
+ }
67
+ const result = await this.config.readModelStore.fetch(this.config, readModelName, readModelID, sequenceKey);
68
+ if (result && result.length > 0) {
69
+ const readModelMetadata = this.config.readModels[readModelName];
70
+ return (0, common_1.createInstance)(readModelMetadata.class, result[0]);
71
+ }
72
+ return undefined;
73
+ }
74
+ getProjectionFunction(projectionMetadata) {
75
+ try {
76
+ return projectionMetadata.class[projectionMetadata.methodName];
77
+ }
78
+ catch {
79
+ throw new Error(`Couldn't load the ReadModel class ${projectionMetadata.class.name}`);
80
+ }
81
+ }
82
+ getUnProjections(entitySnapshotEnvelope) {
83
+ const unProjections = this.entityUnProjections(entitySnapshotEnvelope);
84
+ const projections = this.entityProjections(entitySnapshotEnvelope);
85
+ if ((projections === null || projections === void 0 ? void 0 : projections.length) > 0) {
86
+ if (!unProjections) {
87
+ throw new Error(`Missing UnProjections for entity ${entitySnapshotEnvelope.entityTypeName}`);
88
+ }
89
+ const missingProjection = this.findFirstMissingProjection(projections, unProjections);
90
+ if (missingProjection) {
91
+ throw new Error(`Missing UnProjection for ReadModel ${missingProjection.class.name} with joinKey ${missingProjection.joinKey} for entity ${entitySnapshotEnvelope.entityTypeName}`);
92
+ }
93
+ }
94
+ return unProjections;
95
+ }
96
+ entityProjections(entitySnapshotEnvelope) {
97
+ return this.config.projections[entitySnapshotEnvelope.entityTypeName];
98
+ }
99
+ entityUnProjections(entitySnapshotEnvelope) {
100
+ return this.config.unProjections[entitySnapshotEnvelope.entityTypeName];
101
+ }
102
+ findFirstMissingProjection(sources, to) {
103
+ return sources.find((source) => !this.someProjection(to, source));
104
+ }
105
+ someProjection(sources, to) {
106
+ const contains = (source) => source.class.name === to.class.name && source.joinKey.toString() === to.joinKey.toString();
107
+ return sources.some(contains);
108
+ }
109
+ sequenceKeyForProjection(entity, projectionMetadata) {
110
+ const sequenceKeyName = this.config.readModelSequenceKeys[projectionMetadata.class.name];
111
+ const sequenceKeyValue = entity[sequenceKeyName];
112
+ if (sequenceKeyName && sequenceKeyValue) {
113
+ return { name: sequenceKeyName, value: sequenceKeyValue };
114
+ }
115
+ return undefined;
116
+ }
117
+ async projectEntity(entityInstance, projectionMetadata, entityMetadata, entitySnapshotEnvelope, readModelName, deleteEvent, sequenceKey) {
118
+ const currentReadModels = await this.getReadModels(entityInstance, projectionMetadata, entityMetadata);
119
+ if (currentReadModels && currentReadModels.length > 0) {
120
+ const existingReadModelsProjections = [];
121
+ for (const currentReadModel of currentReadModels) {
122
+ const newProjections = await this.projectionsForReadModels(entitySnapshotEnvelope, readModelName, sequenceKey, projectionMetadata, entityInstance, entityMetadata, deleteEvent, currentReadModel);
123
+ existingReadModelsProjections.push(...newProjections);
124
+ }
125
+ return common_1.Promises.allSettledAndFulfilled(existingReadModelsProjections);
126
+ }
127
+ const newProjections = await this.projectionsForReadModels(entitySnapshotEnvelope, readModelName, sequenceKey, projectionMetadata, entityInstance, entityMetadata, deleteEvent);
128
+ return common_1.Promises.allSettledAndFulfilled(newProjections);
129
+ }
130
+ async projectionsForReadModels(entitySnapshotEnvelope, readModelName, sequenceKey, projectionMetadata, entityInstance, entityMetadata, deleteEvent, currentReadModel) {
131
+ const projections = [];
132
+ const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#projectionsForReadModels');
133
+ if (this.isJoinKeyByEntity(projectionMetadata.joinKey)) {
134
+ const entityJoinKey = entityInstance[projectionMetadata.joinKey];
135
+ if (!entityJoinKey) {
136
+ logger.warn(`Couldn't find the joinKey named ${projectionMetadata} in entity snapshot of ${entityMetadata.class.name}. Skipping...`);
137
+ return [];
138
+ }
139
+ const entitiesJoinKeys = Array.isArray(entityJoinKey) ? entityJoinKey : [entityJoinKey];
140
+ // A new Read Model with JoinKey by entity needs to be projected using a JoinKey with an entity query. We don't have a previous read model, but we have the new id from the entity
141
+ for (const readModelId of entitiesJoinKeys) {
142
+ const readModel = (currentReadModel === null || currentReadModel === void 0 ? void 0 : currentReadModel.id) === readModelId ? currentReadModel : undefined;
143
+ projections.push(this.projectAndStoreReadModelWithRetry(entitySnapshotEnvelope, readModelName, sequenceKey, entityInstance, projectionMetadata, deleteEvent, readModelId, readModel));
144
+ }
145
+ }
146
+ else {
147
+ // A new Read Model with JoinKey by ReadModel needs to be projected using a JoinKey with a read model query. We don't have a previous read model nor the new id as we don't have an entity field
148
+ projections.push(this.projectAndStoreReadModelWithRetry(entitySnapshotEnvelope, readModelName, sequenceKey, entityInstance, projectionMetadata, deleteEvent, currentReadModel === null || currentReadModel === void 0 ? void 0 : currentReadModel.id, currentReadModel));
149
+ }
150
+ return projections;
151
+ }
152
+ async projectAndStoreReadModelWithRetry(entitySnapshotEnvelope, readModelName, sequenceKey, entityInstance, projectionMetadata, deleteEvent, readModelId, currentReadModel) {
153
+ const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#projectAndStoreReadModelWithRetry');
154
+ logger.debug('Projecting entity snapshot ', entitySnapshotEnvelope, ` to build new state of read model ${readModelName} with ID ${(currentReadModel === null || currentReadModel === void 0 ? void 0 : currentReadModel.id) || readModelId}`, sequenceKey ? ` sequencing by ${sequenceKey.name} with value ${sequenceKey.value}` : '');
155
+ return (0, common_1.retryIfError)((tryNumber) => this.applyProjectionToReadModel(entitySnapshotEnvelope, entityInstance, projectionMetadata, deleteEvent, currentReadModel, entitySnapshotEnvelope, readModelId, sequenceKey, tryNumber), common_1.OptimisticConcurrencyUnexpectedVersionError, logger);
156
+ }
157
+ instanceReadModels(readModelName, rawReadModels) {
158
+ if (!(rawReadModels === null || rawReadModels === void 0 ? void 0 : rawReadModels.length)) {
159
+ return [];
160
+ }
161
+ const readModelMetadata = this.config.readModels[readModelName];
162
+ return rawReadModels.map((rawReadModel) => (0, common_1.createInstance)(readModelMetadata.class, rawReadModel));
163
+ }
164
+ async applyProjectionToReadModel(entitySnapshotEnvelope, entity, projectionMetadata, deleteEvent, currentReadModel, lastProjectedEntity, currentReadModelID, sequenceKey, tryNumber) {
165
+ var _a, _b, _c, _d, _e, _f;
166
+ const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#applyProjectionToReadModel');
167
+ const readModelName = projectionMetadata.class.name;
168
+ const readModelID = currentReadModelID !== null && currentReadModelID !== void 0 ? currentReadModelID : currentReadModel === null || currentReadModel === void 0 ? void 0 : currentReadModel.id;
169
+ if (tryNumber && tryNumber > 1) {
170
+ // In case of optimistic concurrency error, we need to fetch the current read model version and retry
171
+ logger.debug(`OptimisticConcurrencyUnexpectedVersionError (version=${(_a = currentReadModel === null || currentReadModel === void 0 ? void 0 : currentReadModel.magekMetadata) === null || _a === void 0 ? void 0 : _a.version} and expectedDatabaseVersion=${(_c = (_b = currentReadModel === null || currentReadModel === void 0 ? void 0 : currentReadModel.magekMetadata) === null || _b === void 0 ? void 0 : _b.version) !== null && _c !== void 0 ? _c : 0}). Looking for an updated version of read model ${readModelName} with ID = ${readModelID}` +
172
+ (sequenceKey ? ` and sequence key ${sequenceKey.name} = ${sequenceKey.value}` : ''));
173
+ currentReadModel = await this.fetchReadModel(readModelName, readModelID, sequenceKey);
174
+ logger.debug(`Current read model ${readModelName} with ID ${readModelID} updated with version = ${(_d = currentReadModel === null || currentReadModel === void 0 ? void 0 : currentReadModel.magekMetadata) === null || _d === void 0 ? void 0 : _d.version}` +
175
+ (sequenceKey ? ` and sequence key ${sequenceKey.name} = ${sequenceKey.value}` : ''));
176
+ }
177
+ let migratedReadModel;
178
+ if (currentReadModel) {
179
+ migratedReadModel = await new read_model_schema_migrator_1.ReadModelSchemaMigrator(this.config).migrate(currentReadModel, readModelName);
180
+ }
181
+ const currentDatabaseVersion = (_f = (_e = migratedReadModel === null || migratedReadModel === void 0 ? void 0 : migratedReadModel.magekMetadata) === null || _e === void 0 ? void 0 : _e.version) !== null && _f !== void 0 ? _f : 0;
182
+ let newReadModel;
183
+ const projectionInfo = {
184
+ reason: deleteEvent ? common_1.ProjectionInfoReason.ENTITY_DELETED : common_1.ProjectionInfoReason.ENTITY_PROJECTED,
185
+ };
186
+ try {
187
+ newReadModel = await this.callProjectionFunction(entitySnapshotEnvelope, projectionMetadata, entity, migratedReadModel, readModelID, projectionInfo);
188
+ }
189
+ catch (e) {
190
+ const globalErrorDispatcher = new global_error_dispatcher_1.MagekGlobalErrorDispatcher(this.config);
191
+ const error = await globalErrorDispatcher.dispatch(new common_1.ProjectionGlobalError(entitySnapshotEnvelope, entity, migratedReadModel, projectionMetadata, e));
192
+ if (error)
193
+ throw error;
194
+ }
195
+ if (newReadModel === common_1.ReadModelAction.Delete) {
196
+ logger.debug(`Deleting read model ${readModelName} with ID ${readModelID}:`, migratedReadModel);
197
+ return this.config.readModelStore.delete(this.config, readModelName, readModelID);
198
+ }
199
+ else if (newReadModel === common_1.ReadModelAction.Nothing) {
200
+ logger.debug(`Skipping actions for ${readModelName} with ID ${readModelID}:`, newReadModel);
201
+ return;
202
+ }
203
+ return await this.store(entity, projectionMetadata, readModelID, readModelName, migratedReadModel, newReadModel, currentDatabaseVersion, lastProjectedEntity);
204
+ }
205
+ async store(entity, projectionMetadata, readModelID, readModelName, migratedReadModel, newReadModel, expectedCurrentDatabaseVersion, lastProjectedEntity) {
206
+ var _a, _b;
207
+ const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#store');
208
+ const schemaVersion = (_b = (_a = migratedReadModel === null || migratedReadModel === void 0 ? void 0 : migratedReadModel.magekMetadata) === null || _a === void 0 ? void 0 : _a.schemaVersion) !== null && _b !== void 0 ? _b : this.config.currentVersionFor(readModelName);
209
+ // Increment the read model version in 1 before storing
210
+ const newReadModelVersion = expectedCurrentDatabaseVersion + 1;
211
+ newReadModel.magekMetadata = {
212
+ ...migratedReadModel === null || migratedReadModel === void 0 ? void 0 : migratedReadModel.magekMetadata,
213
+ version: newReadModelVersion,
214
+ schemaVersion: schemaVersion,
215
+ lastUpdateAt: new Date().toISOString(),
216
+ lastProjectionInfo: {
217
+ entityId: entity.id,
218
+ entityName: lastProjectedEntity === null || lastProjectedEntity === void 0 ? void 0 : lastProjectedEntity.entityTypeName,
219
+ entityUpdatedAt: lastProjectedEntity === null || lastProjectedEntity === void 0 ? void 0 : lastProjectedEntity.createdAt,
220
+ projectionMethod: `${projectionMetadata.class.name}.${projectionMetadata.methodName}`,
221
+ },
222
+ };
223
+ logger.debug(`Storing new version of read model ${readModelName} with ID ${readModelID}, version ${newReadModel.magekMetadata.version} and expected database version ${expectedCurrentDatabaseVersion}:`, newReadModel);
224
+ const envelope = {
225
+ typeName: readModelName,
226
+ value: newReadModel,
227
+ id: readModelID,
228
+ version: newReadModel.magekMetadata.version,
229
+ createdAt: new Date().toISOString(),
230
+ updatedAt: new Date().toISOString(),
231
+ };
232
+ return await this.config.readModelStore.store(this.config, readModelName, envelope);
233
+ }
234
+ async callProjectionFunction(entitySnapshotEnvelope, projectionMetadata, entity, migratedReadModel, readModelID, projectionInfo) {
235
+ try {
236
+ const projectionMetadataJoinKey = projectionMetadata.joinKey;
237
+ const projectionFunction = this.getProjectionFunction(projectionMetadata);
238
+ if (this.isJoinKeyByEntity(projectionMetadataJoinKey)) {
239
+ return Array.isArray(entity[projectionMetadataJoinKey])
240
+ ? projectionFunction(entity, readModelID, migratedReadModel || null, projectionInfo)
241
+ : projectionFunction(entity, migratedReadModel || null, projectionInfo);
242
+ }
243
+ return projectionFunction(entity, readModelID, migratedReadModel || null);
244
+ }
245
+ catch (e) {
246
+ const globalErrorDispatcher = new global_error_dispatcher_1.MagekGlobalErrorDispatcher(this.config);
247
+ const error = await globalErrorDispatcher.dispatch(new common_1.ProjectionGlobalError(entitySnapshotEnvelope, entity, migratedReadModel, projectionMetadata, e));
248
+ if (error)
249
+ throw error;
250
+ }
251
+ return undefined;
252
+ }
253
+ filterForProjection(entity, projectionMetadata, entityMetadata) {
254
+ const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#filterForProjection');
255
+ const projectionMetadataJoinKey = projectionMetadata.joinKey;
256
+ logger.debug(`Calculating filter for projection for ReadModels using Filter ${projectionMetadataJoinKey}`);
257
+ if (this.isJoinKeyByEntity(projectionMetadataJoinKey)) {
258
+ return this.filterForEntityProjection(entity, projectionMetadata, entityMetadata);
259
+ }
260
+ return this.filterForReadModelProjection(entity, projectionMetadata, entityMetadata);
261
+ }
262
+ filterForEntityProjection(entity, projectionMetadata, entityMetadata) {
263
+ const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#filterForEntityProjection');
264
+ const projectionMetadataJoinKey = projectionMetadata.joinKey;
265
+ const entityJoinKey = entity[projectionMetadataJoinKey];
266
+ if (!entityJoinKey) {
267
+ logger.warn(`Couldn't find the joinKey ${projectionMetadata.joinKey} in entity snapshot of ${entityMetadata.class.name}. Skipping...`);
268
+ return;
269
+ }
270
+ const ids = Array.isArray(entityJoinKey) ? entityJoinKey : [entityJoinKey];
271
+ if (!ids || ids.length === 0) {
272
+ logger.debug('No ids found for entity projection. Skipping...');
273
+ return undefined;
274
+ }
275
+ logger.debug(`Filtering for entity projection with ids ${ids}`);
276
+ return {
277
+ id: {
278
+ in: ids,
279
+ },
280
+ };
281
+ }
282
+ filterForReadModelProjection(entity, projectionMetadata, entityMetadata) {
283
+ const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#filterForReadModelProjection');
284
+ const joinKeyForProjection = projectionMetadata.joinKey;
285
+ if (!joinKeyForProjection) {
286
+ logger.warn(`Couldn't find the joinKey ${projectionMetadata.joinKey} in entity snapshot of ${entityMetadata.class.name}. Skipping...`);
287
+ return;
288
+ }
289
+ return joinKeyForProjection(entity);
290
+ }
291
+ isJoinKeyByEntity(projectionMetadataJoinKey) {
292
+ return typeof projectionMetadataJoinKey === 'string';
293
+ }
294
+ }
295
+ exports.ReadModelStore = ReadModelStore;
@@ -0,0 +1,4 @@
1
+ export * from './utilities';
2
+ export * from './jwks-uri-token-verifier';
3
+ export * from './public-key-token-verifier';
4
+ export * from './role-based-token-verifier';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./utilities"), exports);
5
+ tslib_1.__exportStar(require("./jwks-uri-token-verifier"), exports);
6
+ tslib_1.__exportStar(require("./public-key-token-verifier"), exports);
7
+ tslib_1.__exportStar(require("./role-based-token-verifier"), exports);
@@ -0,0 +1,21 @@
1
+ import { DecodedToken } from '@magek/common';
2
+ import type { GetPublicKeyOrSecret } from 'jsonwebtoken';
3
+ import type { JwksClient } from 'jwks-rsa';
4
+ import { RoleBasedTokenVerifier } from './role-based-token-verifier';
5
+ type JwksClientFactory = (jwksUri: string) => JwksClient;
6
+ type KeyResolverBuilder = (client: JwksClient) => GetPublicKeyOrSecret;
7
+ type JwtVerifier = (token: string, issuer: string, key: GetPublicKeyOrSecret) => Promise<DecodedToken>;
8
+ export declare class JwksUriTokenVerifier extends RoleBasedTokenVerifier {
9
+ readonly issuer: string;
10
+ readonly jwksUri: string;
11
+ private readonly buildClient;
12
+ private readonly buildKeyResolver;
13
+ private readonly verifyToken;
14
+ constructor(issuer: string, jwksUri: string, rolesClaim?: string, dependencies?: {
15
+ jwksClientFactory?: JwksClientFactory;
16
+ keyResolverBuilder?: KeyResolverBuilder;
17
+ jwtVerifier?: JwtVerifier;
18
+ });
19
+ verify(token: string): Promise<DecodedToken>;
20
+ }
21
+ export {};
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JwksUriTokenVerifier = void 0;
4
+ const utilities_1 = require("./utilities");
5
+ const role_based_token_verifier_1 = require("./role-based-token-verifier");
6
+ class JwksUriTokenVerifier extends role_based_token_verifier_1.RoleBasedTokenVerifier {
7
+ constructor(issuer, jwksUri, rolesClaim, dependencies) {
8
+ var _a, _b, _c;
9
+ super(rolesClaim);
10
+ this.issuer = issuer;
11
+ this.jwksUri = jwksUri;
12
+ this.buildClient = (_a = dependencies === null || dependencies === void 0 ? void 0 : dependencies.jwksClientFactory) !== null && _a !== void 0 ? _a : utilities_1.getJwksClient;
13
+ this.buildKeyResolver =
14
+ (_b = dependencies === null || dependencies === void 0 ? void 0 : dependencies.keyResolverBuilder) !== null && _b !== void 0 ? _b : ((client) => utilities_1.getKeyWithClient.bind(this, client));
15
+ this.verifyToken = (_c = dependencies === null || dependencies === void 0 ? void 0 : dependencies.jwtVerifier) !== null && _c !== void 0 ? _c : utilities_1.verifyJWT;
16
+ }
17
+ async verify(token) {
18
+ const client = this.buildClient(this.jwksUri);
19
+ const keyResolver = this.buildKeyResolver(client);
20
+ return this.verifyToken(token, this.issuer, keyResolver);
21
+ }
22
+ }
23
+ exports.JwksUriTokenVerifier = JwksUriTokenVerifier;
@@ -0,0 +1,13 @@
1
+ import { DecodedToken } from '@magek/common';
2
+ import { RoleBasedTokenVerifier } from './role-based-token-verifier';
3
+ type JwtVerifier = (token: string, issuer: string, key: string) => Promise<DecodedToken>;
4
+ export declare class PublicKeyTokenVerifier extends RoleBasedTokenVerifier {
5
+ readonly issuer: string;
6
+ readonly publicKeyResolver: Promise<string>;
7
+ private readonly verifyToken;
8
+ constructor(issuer: string, publicKeyResolver: Promise<string>, rolesClaim?: string, dependencies?: {
9
+ jwtVerifier?: JwtVerifier;
10
+ });
11
+ verify(token: string): Promise<DecodedToken>;
12
+ }
13
+ export {};
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PublicKeyTokenVerifier = void 0;
4
+ const utilities_1 = require("./utilities");
5
+ const role_based_token_verifier_1 = require("./role-based-token-verifier");
6
+ class PublicKeyTokenVerifier extends role_based_token_verifier_1.RoleBasedTokenVerifier {
7
+ constructor(issuer, publicKeyResolver, rolesClaim, dependencies) {
8
+ var _a;
9
+ super(rolesClaim);
10
+ this.issuer = issuer;
11
+ this.publicKeyResolver = publicKeyResolver;
12
+ this.verifyToken = (_a = dependencies === null || dependencies === void 0 ? void 0 : dependencies.jwtVerifier) !== null && _a !== void 0 ? _a : utilities_1.verifyJWT;
13
+ }
14
+ async verify(token) {
15
+ const key = await this.publicKeyResolver;
16
+ return this.verifyToken(token, this.issuer, key);
17
+ }
18
+ }
19
+ exports.PublicKeyTokenVerifier = PublicKeyTokenVerifier;
@@ -0,0 +1,8 @@
1
+ import { DecodedToken, TokenVerifier, UserEnvelope } from '@magek/common';
2
+ export declare const DEFAULT_ROLES_CLAIM = "custom:role";
3
+ export declare abstract class RoleBasedTokenVerifier implements TokenVerifier {
4
+ readonly rolesClaim: string;
5
+ constructor(rolesClaim?: string);
6
+ abstract verify(token: string): Promise<DecodedToken>;
7
+ toUserEnvelope(decodedToken: DecodedToken): UserEnvelope;
8
+ }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RoleBasedTokenVerifier = exports.DEFAULT_ROLES_CLAIM = void 0;
4
+ exports.DEFAULT_ROLES_CLAIM = 'custom:role';
5
+ function rolesFromTokenRole(rolesClaim) {
6
+ if (!rolesClaim) {
7
+ return [];
8
+ }
9
+ const roles = Array.isArray(rolesClaim) ? rolesClaim : [rolesClaim];
10
+ return roles.map((role) => {
11
+ if (typeof role !== 'string') {
12
+ throw new Error(`Invalid role format ${role}. Valid format are Array<string> or string`);
13
+ }
14
+ return role;
15
+ });
16
+ }
17
+ class RoleBasedTokenVerifier {
18
+ constructor(rolesClaim = exports.DEFAULT_ROLES_CLAIM) {
19
+ this.rolesClaim = rolesClaim;
20
+ }
21
+ toUserEnvelope(decodedToken) {
22
+ const payload = decodedToken.payload;
23
+ const username = (payload === null || payload === void 0 ? void 0 : payload.email) || (payload === null || payload === void 0 ? void 0 : payload.phone_number) || payload.sub;
24
+ const id = payload.sub;
25
+ const roles = rolesFromTokenRole(payload[this.rolesClaim]);
26
+ return {
27
+ id,
28
+ username,
29
+ roles,
30
+ claims: decodedToken.payload,
31
+ header: decodedToken.header,
32
+ };
33
+ }
34
+ }
35
+ exports.RoleBasedTokenVerifier = RoleBasedTokenVerifier;
@@ -0,0 +1,31 @@
1
+ import { DecodedToken } from '@magek/common';
2
+ import * as jwt from 'jsonwebtoken';
3
+ import { JwksClient } from 'jwks-rsa';
4
+ /**
5
+ * Initializes a jwksRSA client that can be used to get the public key of a JWKS URI using the
6
+ * `getKeyWithClient` function.
7
+ *
8
+ * @param jwksUri The JWKS URI
9
+ * @returns A JwksRSA client
10
+ */
11
+ export declare function getJwksClient(jwksUri: string): JwksClient;
12
+ /**
13
+ * Initializes a function that can be used to get the public key from a JWKS URI with the signature
14
+ * required by the `verifyJWT` function. You can create a client using the `getJwksClient` function.
15
+ *
16
+ * @param client A JwksClient instance
17
+ * @param header The JWT header
18
+ * @param callback The callback function that will be called when the public key is ready
19
+ * @returns A function that can be used to get the public key
20
+ */
21
+ export declare function getKeyWithClient(client: JwksClient, header: jwt.JwtHeader, callback: jwt.SigningKeyCallback): void;
22
+ /**
23
+ * Verifies a JWT token using a key or key resolver function and returns a Magek UserEnvelope.
24
+ *
25
+ * @param token The token to verify
26
+ * @param issuer The issuer of the token
27
+ * @param key The public key to use to verify the token or a function that will resolve a jwksUri to get the public key. The function can be generated using the `getKeyWithClient` function.
28
+ * @param verifier Optional custom JWT verify function (defaults to jsonwebtoken's verify)
29
+ * @returns A promise that resolves to the DecodedToken object
30
+ */
31
+ export declare function verifyJWT(token: string, issuer: string, key: jwt.Secret | jwt.GetPublicKeyOrSecret, verifier?: typeof jwt.verify): Promise<DecodedToken>;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getJwksClient = getJwksClient;
4
+ exports.getKeyWithClient = getKeyWithClient;
5
+ exports.verifyJWT = verifyJWT;
6
+ const jwt = require("jsonwebtoken");
7
+ /**
8
+ * Initializes a jwksRSA client that can be used to get the public key of a JWKS URI using the
9
+ * `getKeyWithClient` function.
10
+ *
11
+ * @param jwksUri The JWKS URI
12
+ * @returns A JwksRSA client
13
+ */
14
+ function getJwksClient(jwksUri) {
15
+ const jwksRSA = require('jwks-rsa'); // Manually loading the default export here to be able to stub it
16
+ return jwksRSA({
17
+ jwksUri,
18
+ cache: true,
19
+ cacheMaxAge: 15 * 60 * 1000, // 15 Minutes cache
20
+ });
21
+ }
22
+ /**
23
+ * Initializes a function that can be used to get the public key from a JWKS URI with the signature
24
+ * required by the `verifyJWT` function. You can create a client using the `getJwksClient` function.
25
+ *
26
+ * @param client A JwksClient instance
27
+ * @param header The JWT header
28
+ * @param callback The callback function that will be called when the public key is ready
29
+ * @returns A function that can be used to get the public key
30
+ */
31
+ function getKeyWithClient(client, header, callback) {
32
+ if (!header.kid) {
33
+ callback(new Error('JWT kid not found'));
34
+ return;
35
+ }
36
+ client.getSigningKey(header.kid, function (err, key) {
37
+ if (err) {
38
+ callback(err);
39
+ return;
40
+ }
41
+ callback(null, key === null || key === void 0 ? void 0 : key.getPublicKey());
42
+ });
43
+ }
44
+ /**
45
+ * Verifies a JWT token using a key or key resolver function and returns a Magek UserEnvelope.
46
+ *
47
+ * @param token The token to verify
48
+ * @param issuer The issuer of the token
49
+ * @param key The public key to use to verify the token or a function that will resolve a jwksUri to get the public key. The function can be generated using the `getKeyWithClient` function.
50
+ * @param verifier Optional custom JWT verify function (defaults to jsonwebtoken's verify)
51
+ * @returns A promise that resolves to the DecodedToken object
52
+ */
53
+ async function verifyJWT(token, issuer, key, verifier = jwt.verify) {
54
+ const sanitizedToken = token.replace('Bearer ', ''); // Remove the 'Bearer' prefix from the token
55
+ return await new Promise((resolve, reject) => {
56
+ verifier(sanitizedToken, key, {
57
+ algorithms: ['RS256'],
58
+ issuer,
59
+ complete: true, // To return headers, payload and other useful token information
60
+ }, (err, decoded) => {
61
+ if (err) {
62
+ return reject(err);
63
+ }
64
+ if (!decoded) {
65
+ return reject(new Error('The token could not be decoded'));
66
+ }
67
+ return resolve(decoded);
68
+ });
69
+ });
70
+ }
@@ -0,0 +1,14 @@
1
+ import { MagekConfig } from '@magek/common';
2
+ export declare class MagekSubscribersNotifier {
3
+ private config;
4
+ private readonly graphQLSchema;
5
+ constructor(config: MagekConfig);
6
+ dispatch(request: unknown): Promise<void>;
7
+ private getReadModelInstance;
8
+ private getSubscriptions;
9
+ private getPubSub;
10
+ private runSubscriptionAndNotify;
11
+ private parseSubscriptionQuery;
12
+ private processSubscriptionsIterator;
13
+ private notifyWithGraphQLResult;
14
+ }