@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.
- package/dist/authorizer.d.ts +7 -0
- package/dist/authorizer.js +35 -0
- package/dist/command-dispatcher.d.ts +8 -0
- package/dist/command-dispatcher.js +55 -0
- package/dist/core-concepts/data-migration/entities/data-migration-entity.d.ts +12 -0
- package/dist/core-concepts/data-migration/entities/data-migration-entity.js +37 -0
- package/dist/core-concepts/data-migration/events/data-migration-finished.d.ts +7 -0
- package/dist/core-concepts/data-migration/events/data-migration-finished.js +13 -0
- package/dist/core-concepts/data-migration/events/data-migration-started.d.ts +7 -0
- package/dist/core-concepts/data-migration/events/data-migration-started.js +13 -0
- package/dist/core-concepts/data-migration/events/entity-migrated.d.ts +9 -0
- package/dist/core-concepts/data-migration/events/entity-migrated.js +15 -0
- package/dist/core-concepts/touch-entity/events/entity-touched.d.ts +7 -0
- package/dist/core-concepts/touch-entity/events/entity-touched.js +13 -0
- package/dist/data-migrations.d.ts +8 -0
- package/dist/data-migrations.js +73 -0
- package/dist/decorators/command.d.ts +19 -0
- package/dist/decorators/command.js +47 -0
- package/dist/decorators/data-migration.d.ts +9 -0
- package/dist/decorators/data-migration.js +25 -0
- package/dist/decorators/entity.d.ts +32 -0
- package/dist/decorators/entity.js +100 -0
- package/dist/decorators/event-handler.d.ts +3 -0
- package/dist/decorators/event-handler.js +18 -0
- package/dist/decorators/event.d.ts +8 -0
- package/dist/decorators/event.js +22 -0
- package/dist/decorators/field-metadata-reader.d.ts +6 -0
- package/dist/decorators/field-metadata-reader.js +221 -0
- package/dist/decorators/global-error-handler.d.ts +2 -0
- package/dist/decorators/global-error-handler.js +15 -0
- package/dist/decorators/global-event-handler.d.ts +3 -0
- package/dist/decorators/global-event-handler.js +9 -0
- package/dist/decorators/health-sensor.d.ts +14 -0
- package/dist/decorators/health-sensor.js +38 -0
- package/dist/decorators/index.d.ts +16 -0
- package/dist/decorators/index.js +19 -0
- package/dist/decorators/metadata.d.ts +13 -0
- package/dist/decorators/metadata.js +55 -0
- package/dist/decorators/non-exposed.d.ts +2 -0
- package/dist/decorators/non-exposed.js +24 -0
- package/dist/decorators/notification.d.ts +35 -0
- package/dist/decorators/notification.js +94 -0
- package/dist/decorators/projects.d.ts +32 -0
- package/dist/decorators/projects.js +87 -0
- package/dist/decorators/query.d.ts +2 -0
- package/dist/decorators/query.js +25 -0
- package/dist/decorators/read-model.d.ts +39 -0
- package/dist/decorators/read-model.js +129 -0
- package/dist/decorators/role.d.ts +6 -0
- package/dist/decorators/role.js +15 -0
- package/dist/decorators/scheduled-command.d.ts +9 -0
- package/dist/decorators/scheduled-command.js +25 -0
- package/dist/decorators/schema-migration.d.ts +36 -0
- package/dist/decorators/schema-migration.js +146 -0
- package/dist/decorators/sequenced-by.d.ts +28 -0
- package/dist/decorators/sequenced-by.js +79 -0
- package/dist/decorators/stage3-utils.d.ts +6 -0
- package/dist/decorators/stage3-utils.js +25 -0
- package/dist/delete-event-dispatcher.d.ts +4 -0
- package/dist/delete-event-dispatcher.js +23 -0
- package/dist/event-dispatcher.d.ts +9 -0
- package/dist/event-dispatcher.js +37 -0
- package/dist/event-processor.d.ts +15 -0
- package/dist/event-processor.js +125 -0
- package/dist/event-search.d.ts +2 -0
- package/dist/event-search.js +26 -0
- package/dist/event-stream-consumer.d.ts +7 -0
- package/dist/event-stream-consumer.js +36 -0
- package/dist/event-stream-producer.d.ts +7 -0
- package/dist/event-stream-producer.js +30 -0
- package/dist/events-reader.d.ts +11 -0
- package/dist/events-reader.js +63 -0
- package/dist/global-error-dispatcher.d.ts +16 -0
- package/dist/global-error-dispatcher.js +109 -0
- package/dist/graphql-dispatcher.d.ts +16 -0
- package/dist/graphql-dispatcher.js +195 -0
- package/dist/importer.d.ts +14 -0
- package/dist/importer.js +49 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +100 -0
- package/dist/injectable/index.d.ts +21 -0
- package/dist/injectable/index.js +2 -0
- package/dist/instrumentation/decorator/trace.d.ts +13 -0
- package/dist/instrumentation/decorator/trace.js +116 -0
- package/dist/instrumentation/index.d.ts +2 -0
- package/dist/instrumentation/index.js +5 -0
- package/dist/instrumentation/trace-notifier.d.ts +3 -0
- package/dist/instrumentation/trace-notifier.js +26 -0
- package/dist/magek.d.ts +42 -0
- package/dist/magek.js +158 -0
- package/dist/query-dispatcher.d.ts +8 -0
- package/dist/query-dispatcher.js +47 -0
- package/dist/read-model-schema-migrator.d.ts +14 -0
- package/dist/read-model-schema-migrator.js +80 -0
- package/dist/read-models-reader.d.ts +31 -0
- package/dist/read-models-reader.js +196 -0
- package/dist/register-handler.d.ts +11 -0
- package/dist/register-handler.js +95 -0
- package/dist/rocket-dispatcher.d.ts +6 -0
- package/dist/rocket-dispatcher.js +21 -0
- package/dist/scheduled-command-dispatcher.d.ts +12 -0
- package/dist/scheduled-command-dispatcher.js +54 -0
- package/dist/schema-migrator.d.ts +12 -0
- package/dist/schema-migrator.js +71 -0
- package/dist/sensor/health/health-indicators/database-events-health-indicator.d.ts +5 -0
- package/dist/sensor/health/health-indicators/database-events-health-indicator.js +26 -0
- package/dist/sensor/health/health-indicators/database-health-indicator.d.ts +5 -0
- package/dist/sensor/health/health-indicators/database-health-indicator.js +29 -0
- package/dist/sensor/health/health-indicators/database-read-models-health-indicator.d.ts +5 -0
- package/dist/sensor/health/health-indicators/database-read-models-health-indicator.js +26 -0
- package/dist/sensor/health/health-indicators/default-health-indicators.d.ts +5 -0
- package/dist/sensor/health/health-indicators/default-health-indicators.js +39 -0
- package/dist/sensor/health/health-indicators/function-health-indicator.d.ts +5 -0
- package/dist/sensor/health/health-indicators/function-health-indicator.js +30 -0
- package/dist/sensor/health/health-indicators/health-indicator.d.ts +5 -0
- package/dist/sensor/health/health-indicators/health-indicator.js +30 -0
- package/dist/sensor/health/health-indicators/index.d.ts +3 -0
- package/dist/sensor/health/health-indicators/index.js +6 -0
- package/dist/sensor/health/health-indicators/os-info.d.ts +14 -0
- package/dist/sensor/health/health-indicators/os-info.js +38 -0
- package/dist/sensor/health/health-indicators/rockets-health-indicator.d.ts +5 -0
- package/dist/sensor/health/health-indicators/rockets-health-indicator.js +57 -0
- package/dist/sensor/health/health-indicators/version.d.ts +2 -0
- package/dist/sensor/health/health-indicators/version.js +24 -0
- package/dist/sensor/health/health-service.d.ts +22 -0
- package/dist/sensor/health/health-service.js +117 -0
- package/dist/sensor/health/health-utils.d.ts +7 -0
- package/dist/sensor/health/health-utils.js +53 -0
- package/dist/sensor/health/index.d.ts +3 -0
- package/dist/sensor/health/index.js +6 -0
- package/dist/sensor/index.d.ts +1 -0
- package/dist/sensor/index.js +4 -0
- package/dist/services/event-store.d.ts +27 -0
- package/dist/services/event-store.js +260 -0
- package/dist/services/filter-helpers.d.ts +3 -0
- package/dist/services/filter-helpers.js +19 -0
- package/dist/services/graphql/common.d.ts +26 -0
- package/dist/services/graphql/common.js +53 -0
- package/dist/services/graphql/graphql-generator.d.ts +46 -0
- package/dist/services/graphql/graphql-generator.js +269 -0
- package/dist/services/graphql/graphql-mutation-generator.d.ts +12 -0
- package/dist/services/graphql/graphql-mutation-generator.js +25 -0
- package/dist/services/graphql/graphql-query-generator.d.ts +22 -0
- package/dist/services/graphql/graphql-query-generator.js +39 -0
- package/dist/services/graphql/graphql-subcriptions-generator.d.ts +17 -0
- package/dist/services/graphql/graphql-subcriptions-generator.js +60 -0
- package/dist/services/graphql/graphql-type-informer.d.ts +23 -0
- package/dist/services/graphql/graphql-type-informer.js +160 -0
- package/dist/services/graphql/query-generators/graphql-query-by-keys-generator.d.ts +14 -0
- package/dist/services/graphql/query-generators/graphql-query-by-keys-generator.js +48 -0
- package/dist/services/graphql/query-generators/graphql-query-events-generator.d.ts +11 -0
- package/dist/services/graphql/query-generators/graphql-query-events-generator.js +68 -0
- package/dist/services/graphql/query-generators/graphql-query-filters-generator.d.ts +14 -0
- package/dist/services/graphql/query-generators/graphql-query-filters-generator.js +31 -0
- package/dist/services/graphql/query-generators/graphql-query-generator.d.ts +12 -0
- package/dist/services/graphql/query-generators/graphql-query-generator.js +17 -0
- package/dist/services/graphql/query-generators/graphql-query-listed-generator.d.ts +16 -0
- package/dist/services/graphql/query-generators/graphql-query-listed-generator.js +65 -0
- package/dist/services/graphql/query-helpers/graphql-handled-fields-generator.d.ts +15 -0
- package/dist/services/graphql/query-helpers/graphql-handled-fields-generator.js +65 -0
- package/dist/services/graphql/query-helpers/graphql-query-filter-arguments-builder.d.ts +13 -0
- package/dist/services/graphql/query-helpers/graphql-query-filter-arguments-builder.js +169 -0
- package/dist/services/graphql/query-helpers/graphql-query-filter-fields-builder.d.ts +11 -0
- package/dist/services/graphql/query-helpers/graphql-query-filter-fields-builder.js +28 -0
- package/dist/services/graphql/query-helpers/graphql-query-sort-builder.d.ts +12 -0
- package/dist/services/graphql/query-helpers/graphql-query-sort-builder.js +61 -0
- package/dist/services/graphql/websocket-protocol/graphql-websocket-protocol.d.ts +20 -0
- package/dist/services/graphql/websocket-protocol/graphql-websocket-protocol.js +127 -0
- package/dist/services/pub-sub/noop-read-model-pub-sub.d.ts +5 -0
- package/dist/services/pub-sub/noop-read-model-pub-sub.js +10 -0
- package/dist/services/pub-sub/read-model-pub-sub.d.ts +9 -0
- package/dist/services/pub-sub/read-model-pub-sub.js +112 -0
- package/dist/services/raw-events-parser.d.ts +5 -0
- package/dist/services/raw-events-parser.js +44 -0
- package/dist/services/read-model-searcher.d.ts +2 -0
- package/dist/services/read-model-searcher.js +11 -0
- package/dist/services/read-model-store.d.ts +41 -0
- package/dist/services/read-model-store.js +295 -0
- package/dist/services/token-verifiers/index.d.ts +4 -0
- package/dist/services/token-verifiers/index.js +7 -0
- package/dist/services/token-verifiers/jwks-uri-token-verifier.d.ts +21 -0
- package/dist/services/token-verifiers/jwks-uri-token-verifier.js +23 -0
- package/dist/services/token-verifiers/public-key-token-verifier.d.ts +13 -0
- package/dist/services/token-verifiers/public-key-token-verifier.js +19 -0
- package/dist/services/token-verifiers/role-based-token-verifier.d.ts +8 -0
- package/dist/services/token-verifiers/role-based-token-verifier.js +35 -0
- package/dist/services/token-verifiers/utilities.d.ts +31 -0
- package/dist/services/token-verifiers/utilities.js +70 -0
- package/dist/subscribers-notifier.d.ts +14 -0
- package/dist/subscribers-notifier.js +109 -0
- package/dist/token-verifier.d.ts +11 -0
- package/dist/token-verifier.js +46 -0
- package/dist/touch-entity-handler.d.ts +4 -0
- package/dist/touch-entity-handler.js +16 -0
- 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,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
|
+
}
|