@magek/core 0.0.6 → 0.0.8
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.js +1 -1
- package/dist/command-dispatcher.js +47 -41
- package/dist/core-concepts/data-migration/entities/data-migration-entity.js +5 -2
- package/dist/core-concepts/data-migration/events/data-migration-finished.js +3 -1
- package/dist/core-concepts/data-migration/events/data-migration-started.js +3 -1
- package/dist/core-concepts/data-migration/events/entity-migrated.js +4 -0
- package/dist/core-concepts/touch-entity/events/entity-touched.js +2 -0
- package/dist/data-migrations.js +59 -54
- package/dist/decorators/command.d.ts +4 -11
- package/dist/decorators/command.js +10 -18
- package/dist/decorators/data-migration.d.ts +4 -1
- package/dist/decorators/data-migration.js +3 -1
- package/dist/decorators/decorator-types.d.ts +50 -0
- package/dist/decorators/decorator-types.js +11 -0
- package/dist/decorators/decorator-utils.d.ts +1 -0
- package/dist/decorators/decorator-utils.js +2 -0
- package/dist/decorators/entity.d.ts +7 -15
- package/dist/decorators/entity.js +27 -37
- package/dist/decorators/event-handler.d.ts +11 -1
- package/dist/decorators/event-handler.js +13 -1
- package/dist/decorators/event.d.ts +4 -1
- package/dist/decorators/event.js +3 -2
- package/dist/decorators/field-metadata-reader.d.ts +8 -3
- package/dist/decorators/field-metadata-reader.js +61 -62
- package/dist/decorators/field.d.ts +23 -0
- package/dist/decorators/field.js +41 -0
- package/dist/decorators/global-error-handler.d.ts +10 -1
- package/dist/decorators/global-error-handler.js +9 -1
- package/dist/decorators/global-event-handler.d.ts +10 -1
- package/dist/decorators/global-event-handler.js +9 -1
- package/dist/decorators/health-sensor.d.ts +4 -1
- package/dist/decorators/health-sensor.js +3 -2
- package/dist/decorators/index.d.ts +4 -0
- package/dist/decorators/index.js +3 -0
- package/dist/decorators/metadata.d.ts +17 -1
- package/dist/decorators/metadata.js +22 -22
- package/dist/decorators/non-exposed.d.ts +13 -2
- package/dist/decorators/non-exposed.js +22 -20
- package/dist/decorators/notification.d.ts +6 -18
- package/dist/decorators/notification.js +10 -50
- package/dist/decorators/projects.d.ts +5 -18
- package/dist/decorators/projects.js +23 -54
- package/dist/decorators/query.d.ts +11 -1
- package/dist/decorators/query.js +18 -4
- package/dist/decorators/read-model.d.ts +13 -27
- package/dist/decorators/read-model.js +45 -77
- package/dist/decorators/returns.d.ts +37 -0
- package/dist/decorators/returns.js +154 -0
- package/dist/decorators/role.d.ts +9 -3
- package/dist/decorators/role.js +8 -3
- package/dist/decorators/scheduled-command.d.ts +4 -1
- package/dist/decorators/scheduled-command.js +3 -1
- package/dist/decorators/schema-migration.d.ts +11 -27
- package/dist/decorators/schema-migration.js +32 -77
- package/dist/decorators/sequenced-by.d.ts +7 -25
- package/dist/decorators/sequenced-by.js +11 -71
- package/dist/event-dispatcher.js +29 -24
- package/dist/event-processor.js +107 -103
- package/dist/event-stream-consumer.js +25 -20
- package/dist/event-stream-producer.js +22 -17
- package/dist/events-reader.js +1 -0
- package/dist/global-error-dispatcher.js +3 -2
- package/dist/graphql-dispatcher.js +161 -156
- package/dist/index.js +4 -0
- package/dist/instrumentation/decorator/trace.d.ts +11 -3
- package/dist/instrumentation/decorator/trace.js +17 -71
- package/dist/magek.js +2 -2
- package/dist/query-dispatcher.js +2 -0
- package/dist/read-model-schema-migrator.js +71 -68
- package/dist/read-models-reader.js +178 -180
- package/dist/register-handler.js +3 -3
- package/dist/scheduled-command-dispatcher.js +48 -42
- package/dist/schema-migrator.js +63 -59
- package/dist/sensor/health/health-service.js +2 -1
- package/dist/services/event-store.js +221 -224
- package/dist/services/graphql/graphql-generator.js +11 -8
- package/dist/services/graphql/graphql-mutation-generator.js +4 -0
- package/dist/services/graphql/graphql-query-generator.js +14 -0
- package/dist/services/graphql/graphql-subcriptions-generator.js +7 -0
- package/dist/services/graphql/graphql-type-informer.js +4 -3
- package/dist/services/graphql/query-generators/graphql-query-by-keys-generator.js +4 -0
- package/dist/services/graphql/query-generators/graphql-query-events-generator.js +3 -0
- package/dist/services/graphql/query-generators/graphql-query-filters-generator.js +6 -0
- package/dist/services/graphql/query-generators/graphql-query-generator.js +4 -0
- package/dist/services/graphql/query-generators/graphql-query-listed-generator.js +7 -0
- package/dist/services/graphql/query-helpers/graphql-handled-fields-generator.js +5 -2
- package/dist/services/graphql/query-helpers/graphql-query-filter-arguments-builder.js +3 -0
- package/dist/services/graphql/query-helpers/graphql-query-filter-fields-builder.js +4 -0
- package/dist/services/graphql/query-helpers/graphql-query-sort-builder.js +4 -2
- package/dist/services/graphql/websocket-protocol/graphql-websocket-protocol.js +5 -3
- package/dist/services/pub-sub/read-model-pub-sub.js +1 -0
- package/dist/services/raw-events-parser.js +1 -1
- package/dist/services/read-model-store.js +20 -20
- package/dist/services/token-verifiers/jwks-uri-token-verifier.js +8 -4
- package/dist/services/token-verifiers/public-key-token-verifier.js +4 -2
- package/dist/services/token-verifiers/role-based-token-verifier.js +2 -1
- package/dist/services/token-verifiers/utilities.js +1 -1
- package/dist/subscribers-notifier.js +99 -92
- package/dist/token-verifier.js +2 -1
- package/dist/utils/promises.d.ts +25 -0
- package/dist/utils/promises.js +43 -0
- package/package.json +4 -4
- package/dist/decorators/stage3-utils.d.ts +0 -6
- package/dist/decorators/stage3-utils.js +0 -25
|
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.GraphqlQueryByKeysGenerator = void 0;
|
|
4
4
|
const graphql_1 = require("graphql");
|
|
5
5
|
class GraphqlQueryByKeysGenerator {
|
|
6
|
+
config;
|
|
7
|
+
readModels;
|
|
8
|
+
typeInformer;
|
|
9
|
+
byIDResolverBuilder;
|
|
6
10
|
constructor(config, readModels, typeInformer, byIDResolverBuilder) {
|
|
7
11
|
this.config = config;
|
|
8
12
|
this.readModels = readModels;
|
|
@@ -5,6 +5,9 @@ const graphql_1 = require("graphql");
|
|
|
5
5
|
const common_1 = require("../common");
|
|
6
6
|
const graphql_scalars_1 = require("graphql-scalars");
|
|
7
7
|
class GraphqlQueryEventsGenerator {
|
|
8
|
+
config;
|
|
9
|
+
byIDResolverBuilder;
|
|
10
|
+
eventsResolver;
|
|
8
11
|
constructor(config, byIDResolverBuilder, eventsResolver) {
|
|
9
12
|
this.config = config;
|
|
10
13
|
this.byIDResolverBuilder = byIDResolverBuilder;
|
|
@@ -5,6 +5,12 @@ const graphql_1 = require("graphql");
|
|
|
5
5
|
const inflected = require("inflected");
|
|
6
6
|
const graphql_query_filter_fields_builder_1 = require("../query-helpers/graphql-query-filter-fields-builder");
|
|
7
7
|
class GraphqlQueryFiltersGenerator {
|
|
8
|
+
readModels;
|
|
9
|
+
typeInformer;
|
|
10
|
+
filterResolverBuilder;
|
|
11
|
+
generatedFiltersByTypeName;
|
|
12
|
+
config;
|
|
13
|
+
graphqlQueryFilterFieldsBuilder;
|
|
8
14
|
constructor(readModels, typeInformer, filterResolverBuilder, generatedFiltersByTypeName = {}, config) {
|
|
9
15
|
this.readModels = readModels;
|
|
10
16
|
this.typeInformer = typeInformer;
|
|
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.GraphqlQueryGenerator = void 0;
|
|
4
4
|
const graphql_handled_fields_generator_1 = require("../query-helpers/graphql-handled-fields-generator");
|
|
5
5
|
class GraphqlQueryGenerator {
|
|
6
|
+
targetTypes;
|
|
7
|
+
typeInformer;
|
|
8
|
+
queryResolveBuilder;
|
|
9
|
+
config;
|
|
6
10
|
constructor(targetTypes, typeInformer, queryResolveBuilder, config) {
|
|
7
11
|
this.targetTypes = targetTypes;
|
|
8
12
|
this.typeInformer = typeInformer;
|
|
@@ -7,6 +7,13 @@ const graphql_scalars_1 = require("graphql-scalars");
|
|
|
7
7
|
const graphql_query_sort_builder_1 = require("../query-helpers/graphql-query-sort-builder");
|
|
8
8
|
const graphql_query_filter_arguments_builder_1 = require("../query-helpers/graphql-query-filter-arguments-builder");
|
|
9
9
|
class GraphqlQueryListedGenerator {
|
|
10
|
+
readModels;
|
|
11
|
+
typeInformer;
|
|
12
|
+
filterResolverBuilder;
|
|
13
|
+
generatedFiltersByTypeName;
|
|
14
|
+
config;
|
|
15
|
+
graphqlQueryFilterArgumentsBuilder;
|
|
16
|
+
graphqlQuerySortBuilder;
|
|
10
17
|
constructor(readModels, typeInformer, filterResolverBuilder, generatedFiltersByTypeName = {}, config) {
|
|
11
18
|
this.readModels = readModels;
|
|
12
19
|
this.typeInformer = typeInformer;
|
|
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.GraphQLHandledFieldsGenerator = void 0;
|
|
4
4
|
const graphql_1 = require("graphql");
|
|
5
5
|
class GraphQLHandledFieldsGenerator {
|
|
6
|
+
targetTypes;
|
|
7
|
+
typeInformer;
|
|
8
|
+
resolver;
|
|
9
|
+
config;
|
|
6
10
|
constructor(targetTypes, typeInformer, resolver, config) {
|
|
7
11
|
this.targetTypes = targetTypes;
|
|
8
12
|
this.typeInformer = typeInformer;
|
|
@@ -37,8 +41,7 @@ class GraphQLHandledFieldsGenerator {
|
|
|
37
41
|
};
|
|
38
42
|
}
|
|
39
43
|
static getHandleMethodMetadata(metadata) {
|
|
40
|
-
|
|
41
|
-
let handleMethodMetadata = (_a = metadata.methods.find((m) => m.name === 'handle')) === null || _a === void 0 ? void 0 : _a.typeInfo;
|
|
44
|
+
let handleMethodMetadata = metadata.methods.find((m) => m.name === 'handle')?.typeInfo;
|
|
42
45
|
if (handleMethodMetadata && handleMethodMetadata.typeName === 'Promise') {
|
|
43
46
|
// If async function, return type is wrapped in a Promise
|
|
44
47
|
handleMethodMetadata = handleMethodMetadata.parameters[0];
|
|
@@ -7,6 +7,9 @@ const graphql_scalars_1 = require("graphql-scalars");
|
|
|
7
7
|
const common_1 = require("@magek/common");
|
|
8
8
|
const common_2 = require("../common");
|
|
9
9
|
class GraphqlQueryFilterArgumentsBuilder {
|
|
10
|
+
typeInformer;
|
|
11
|
+
generatedFiltersByTypeName;
|
|
12
|
+
config;
|
|
10
13
|
constructor(typeInformer, generatedFiltersByTypeName = {}, config) {
|
|
11
14
|
this.typeInformer = typeInformer;
|
|
12
15
|
this.generatedFiltersByTypeName = generatedFiltersByTypeName;
|
|
@@ -4,6 +4,10 @@ exports.GraphqlQueryFilterFieldsBuilder = void 0;
|
|
|
4
4
|
const graphql_1 = require("graphql");
|
|
5
5
|
const graphql_query_filter_arguments_builder_1 = require("./graphql-query-filter-arguments-builder");
|
|
6
6
|
class GraphqlQueryFilterFieldsBuilder {
|
|
7
|
+
typeInformer;
|
|
8
|
+
generatedFiltersByTypeName;
|
|
9
|
+
config;
|
|
10
|
+
graphqlQueryFilterArgumentsBuilder;
|
|
7
11
|
constructor(typeInformer, generatedFiltersByTypeName = {}, config) {
|
|
8
12
|
this.typeInformer = typeInformer;
|
|
9
13
|
this.generatedFiltersByTypeName = generatedFiltersByTypeName;
|
|
@@ -5,11 +5,13 @@ const graphql_1 = require("graphql");
|
|
|
5
5
|
const metadata_1 = require("../../../decorators/metadata");
|
|
6
6
|
const common_1 = require("../common");
|
|
7
7
|
class GraphqlQuerySortBuilder {
|
|
8
|
+
typeInformer;
|
|
9
|
+
config;
|
|
10
|
+
generatedSortByByTypeName = {};
|
|
11
|
+
orderType = (0, common_1.buildGraphqlSimpleEnumFor)('orderProperty', ['ASC', 'DESC']);
|
|
8
12
|
constructor(typeInformer, config) {
|
|
9
13
|
this.typeInformer = typeInformer;
|
|
10
14
|
this.config = config;
|
|
11
|
-
this.generatedSortByByTypeName = {};
|
|
12
|
-
this.orderType = (0, common_1.buildGraphqlSimpleEnumFor)('orderProperty', ['ASC', 'DESC']);
|
|
13
15
|
}
|
|
14
16
|
generateSortArguments(type, excludeProps) {
|
|
15
17
|
const metadata = (0, metadata_1.getClassMetadata)(type);
|
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.GraphQLWebsocketHandler = void 0;
|
|
4
4
|
const common_1 = require("@magek/common");
|
|
5
5
|
class GraphQLWebsocketHandler {
|
|
6
|
+
config;
|
|
7
|
+
callbacks;
|
|
8
|
+
tokenVerifier;
|
|
6
9
|
constructor(config, callbacks, tokenVerifier) {
|
|
7
10
|
this.config = config;
|
|
8
11
|
this.callbacks = callbacks;
|
|
@@ -47,10 +50,9 @@ class GraphQLWebsocketHandler {
|
|
|
47
50
|
}
|
|
48
51
|
}
|
|
49
52
|
async handleInit(connectionID, clientMessage) {
|
|
50
|
-
var _a;
|
|
51
53
|
const logger = (0, common_1.getLogger)(this.config, 'GraphQLWebsocketHandler#handleInit');
|
|
52
54
|
let userEnvelope;
|
|
53
|
-
if (
|
|
55
|
+
if (clientMessage.payload?.Authorization) {
|
|
54
56
|
userEnvelope = await this.tokenVerifier.verify(clientMessage.payload.Authorization);
|
|
55
57
|
}
|
|
56
58
|
const nowEpoch = Math.floor(new Date().getTime() / 1000);
|
|
@@ -92,7 +94,7 @@ class GraphQLWebsocketHandler {
|
|
|
92
94
|
logger.debug('Found connection data: ', connectionData);
|
|
93
95
|
return {
|
|
94
96
|
...envelope,
|
|
95
|
-
currentUser: connectionData
|
|
97
|
+
currentUser: connectionData?.user,
|
|
96
98
|
value: {
|
|
97
99
|
...message.payload,
|
|
98
100
|
id: message.id,
|
|
@@ -32,7 +32,7 @@ function isEventKind(envelope) {
|
|
|
32
32
|
return envelope.kind == 'event';
|
|
33
33
|
}
|
|
34
34
|
function isNotDeleted(envelope) {
|
|
35
|
-
return !
|
|
35
|
+
return !envelope?.deletedAt;
|
|
36
36
|
}
|
|
37
37
|
function groupByEntity(envelopesPerEntity, envelope) {
|
|
38
38
|
const entityKey = `${envelope.entityTypeName}-${envelope.entityID}`;
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ReadModelStore = void 0;
|
|
4
4
|
const common_1 = require("@magek/common");
|
|
5
|
+
const promises_1 = require("../utils/promises");
|
|
5
6
|
const global_error_dispatcher_1 = require("../global-error-dispatcher");
|
|
6
7
|
const read_model_searcher_1 = require("./read-model-searcher");
|
|
7
8
|
const read_model_schema_migrator_1 = require("../read-model-schema-migrator");
|
|
8
9
|
class ReadModelStore {
|
|
10
|
+
config;
|
|
9
11
|
constructor(config) {
|
|
10
12
|
this.config = config;
|
|
11
13
|
}
|
|
@@ -27,7 +29,7 @@ class ReadModelStore {
|
|
|
27
29
|
const sequenceKey = this.sequenceKeyForProjection(entityInstance, projectionMetadata);
|
|
28
30
|
return this.projectEntity(entityInstance, projectionMetadata, entityMetadata, entitySnapshotEnvelope, readModelName, deleteEvent, sequenceKey);
|
|
29
31
|
});
|
|
30
|
-
await
|
|
32
|
+
await promises_1.Promises.allSettledAndFulfilled(projectReadModelPromises);
|
|
31
33
|
}
|
|
32
34
|
/**
|
|
33
35
|
* Gets the read models for a given entity instance using the projection metadata
|
|
@@ -82,7 +84,7 @@ class ReadModelStore {
|
|
|
82
84
|
getUnProjections(entitySnapshotEnvelope) {
|
|
83
85
|
const unProjections = this.entityUnProjections(entitySnapshotEnvelope);
|
|
84
86
|
const projections = this.entityProjections(entitySnapshotEnvelope);
|
|
85
|
-
if (
|
|
87
|
+
if (projections?.length > 0) {
|
|
86
88
|
if (!unProjections) {
|
|
87
89
|
throw new Error(`Missing UnProjections for entity ${entitySnapshotEnvelope.entityTypeName}`);
|
|
88
90
|
}
|
|
@@ -122,10 +124,10 @@ class ReadModelStore {
|
|
|
122
124
|
const newProjections = await this.projectionsForReadModels(entitySnapshotEnvelope, readModelName, sequenceKey, projectionMetadata, entityInstance, entityMetadata, deleteEvent, currentReadModel);
|
|
123
125
|
existingReadModelsProjections.push(...newProjections);
|
|
124
126
|
}
|
|
125
|
-
return
|
|
127
|
+
return promises_1.Promises.allSettledAndFulfilled(existingReadModelsProjections);
|
|
126
128
|
}
|
|
127
129
|
const newProjections = await this.projectionsForReadModels(entitySnapshotEnvelope, readModelName, sequenceKey, projectionMetadata, entityInstance, entityMetadata, deleteEvent);
|
|
128
|
-
return
|
|
130
|
+
return promises_1.Promises.allSettledAndFulfilled(newProjections);
|
|
129
131
|
}
|
|
130
132
|
async projectionsForReadModels(entitySnapshotEnvelope, readModelName, sequenceKey, projectionMetadata, entityInstance, entityMetadata, deleteEvent, currentReadModel) {
|
|
131
133
|
const projections = [];
|
|
@@ -139,46 +141,45 @@ class ReadModelStore {
|
|
|
139
141
|
const entitiesJoinKeys = Array.isArray(entityJoinKey) ? entityJoinKey : [entityJoinKey];
|
|
140
142
|
// 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
143
|
for (const readModelId of entitiesJoinKeys) {
|
|
142
|
-
const readModel =
|
|
144
|
+
const readModel = currentReadModel?.id === readModelId ? currentReadModel : undefined;
|
|
143
145
|
projections.push(this.projectAndStoreReadModelWithRetry(entitySnapshotEnvelope, readModelName, sequenceKey, entityInstance, projectionMetadata, deleteEvent, readModelId, readModel));
|
|
144
146
|
}
|
|
145
147
|
}
|
|
146
148
|
else {
|
|
147
149
|
// 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
|
|
150
|
+
projections.push(this.projectAndStoreReadModelWithRetry(entitySnapshotEnvelope, readModelName, sequenceKey, entityInstance, projectionMetadata, deleteEvent, currentReadModel?.id, currentReadModel));
|
|
149
151
|
}
|
|
150
152
|
return projections;
|
|
151
153
|
}
|
|
152
154
|
async projectAndStoreReadModelWithRetry(entitySnapshotEnvelope, readModelName, sequenceKey, entityInstance, projectionMetadata, deleteEvent, readModelId, currentReadModel) {
|
|
153
155
|
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 ${
|
|
156
|
+
logger.debug('Projecting entity snapshot ', entitySnapshotEnvelope, ` to build new state of read model ${readModelName} with ID ${currentReadModel?.id || readModelId}`, sequenceKey ? ` sequencing by ${sequenceKey.name} with value ${sequenceKey.value}` : '');
|
|
155
157
|
return (0, common_1.retryIfError)((tryNumber) => this.applyProjectionToReadModel(entitySnapshotEnvelope, entityInstance, projectionMetadata, deleteEvent, currentReadModel, entitySnapshotEnvelope, readModelId, sequenceKey, tryNumber), common_1.OptimisticConcurrencyUnexpectedVersionError, logger);
|
|
156
158
|
}
|
|
157
159
|
instanceReadModels(readModelName, rawReadModels) {
|
|
158
|
-
if (!
|
|
160
|
+
if (!rawReadModels?.length) {
|
|
159
161
|
return [];
|
|
160
162
|
}
|
|
161
163
|
const readModelMetadata = this.config.readModels[readModelName];
|
|
162
164
|
return rawReadModels.map((rawReadModel) => (0, common_1.createInstance)(readModelMetadata.class, rawReadModel));
|
|
163
165
|
}
|
|
164
166
|
async applyProjectionToReadModel(entitySnapshotEnvelope, entity, projectionMetadata, deleteEvent, currentReadModel, lastProjectedEntity, currentReadModelID, sequenceKey, tryNumber) {
|
|
165
|
-
var _a, _b, _c, _d, _e, _f;
|
|
166
167
|
const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#applyProjectionToReadModel');
|
|
167
168
|
const readModelName = projectionMetadata.class.name;
|
|
168
|
-
const readModelID = currentReadModelID
|
|
169
|
+
const readModelID = currentReadModelID ?? currentReadModel?.id;
|
|
169
170
|
if (tryNumber && tryNumber > 1) {
|
|
170
171
|
// In case of optimistic concurrency error, we need to fetch the current read model version and retry
|
|
171
|
-
logger.debug(`OptimisticConcurrencyUnexpectedVersionError (version=${
|
|
172
|
+
logger.debug(`OptimisticConcurrencyUnexpectedVersionError (version=${currentReadModel?.magekMetadata?.version} and expectedDatabaseVersion=${currentReadModel?.magekMetadata?.version ?? 0}). Looking for an updated version of read model ${readModelName} with ID = ${readModelID}` +
|
|
172
173
|
(sequenceKey ? ` and sequence key ${sequenceKey.name} = ${sequenceKey.value}` : ''));
|
|
173
174
|
currentReadModel = await this.fetchReadModel(readModelName, readModelID, sequenceKey);
|
|
174
|
-
logger.debug(`Current read model ${readModelName} with ID ${readModelID} updated with version = ${
|
|
175
|
+
logger.debug(`Current read model ${readModelName} with ID ${readModelID} updated with version = ${currentReadModel?.magekMetadata?.version}` +
|
|
175
176
|
(sequenceKey ? ` and sequence key ${sequenceKey.name} = ${sequenceKey.value}` : ''));
|
|
176
177
|
}
|
|
177
178
|
let migratedReadModel;
|
|
178
179
|
if (currentReadModel) {
|
|
179
180
|
migratedReadModel = await new read_model_schema_migrator_1.ReadModelSchemaMigrator(this.config).migrate(currentReadModel, readModelName);
|
|
180
181
|
}
|
|
181
|
-
const currentDatabaseVersion =
|
|
182
|
+
const currentDatabaseVersion = migratedReadModel?.magekMetadata?.version ?? 0;
|
|
182
183
|
let newReadModel;
|
|
183
184
|
const projectionInfo = {
|
|
184
185
|
reason: deleteEvent ? common_1.ProjectionInfoReason.ENTITY_DELETED : common_1.ProjectionInfoReason.ENTITY_PROJECTED,
|
|
@@ -192,31 +193,30 @@ class ReadModelStore {
|
|
|
192
193
|
if (error)
|
|
193
194
|
throw error;
|
|
194
195
|
}
|
|
195
|
-
if (newReadModel === common_1.
|
|
196
|
+
if (newReadModel === common_1.ProjectionAction.Delete) {
|
|
196
197
|
logger.debug(`Deleting read model ${readModelName} with ID ${readModelID}:`, migratedReadModel);
|
|
197
198
|
return this.config.readModelStore.delete(this.config, readModelName, readModelID);
|
|
198
199
|
}
|
|
199
|
-
else if (newReadModel === common_1.
|
|
200
|
+
else if (newReadModel === common_1.ProjectionAction.Skip) {
|
|
200
201
|
logger.debug(`Skipping actions for ${readModelName} with ID ${readModelID}:`, newReadModel);
|
|
201
202
|
return;
|
|
202
203
|
}
|
|
203
204
|
return await this.store(entity, projectionMetadata, readModelID, readModelName, migratedReadModel, newReadModel, currentDatabaseVersion, lastProjectedEntity);
|
|
204
205
|
}
|
|
205
206
|
async store(entity, projectionMetadata, readModelID, readModelName, migratedReadModel, newReadModel, expectedCurrentDatabaseVersion, lastProjectedEntity) {
|
|
206
|
-
var _a, _b;
|
|
207
207
|
const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#store');
|
|
208
|
-
const schemaVersion =
|
|
208
|
+
const schemaVersion = migratedReadModel?.magekMetadata?.schemaVersion ?? this.config.currentVersionFor(readModelName);
|
|
209
209
|
// Increment the read model version in 1 before storing
|
|
210
210
|
const newReadModelVersion = expectedCurrentDatabaseVersion + 1;
|
|
211
211
|
newReadModel.magekMetadata = {
|
|
212
|
-
...migratedReadModel
|
|
212
|
+
...migratedReadModel?.magekMetadata,
|
|
213
213
|
version: newReadModelVersion,
|
|
214
214
|
schemaVersion: schemaVersion,
|
|
215
215
|
lastUpdateAt: new Date().toISOString(),
|
|
216
216
|
lastProjectionInfo: {
|
|
217
217
|
entityId: entity.id,
|
|
218
|
-
entityName: lastProjectedEntity
|
|
219
|
-
entityUpdatedAt: lastProjectedEntity
|
|
218
|
+
entityName: lastProjectedEntity?.entityTypeName,
|
|
219
|
+
entityUpdatedAt: lastProjectedEntity?.createdAt,
|
|
220
220
|
projectionMethod: `${projectionMetadata.class.name}.${projectionMetadata.methodName}`,
|
|
221
221
|
},
|
|
222
222
|
};
|
|
@@ -4,15 +4,19 @@ exports.JwksUriTokenVerifier = void 0;
|
|
|
4
4
|
const utilities_1 = require("./utilities");
|
|
5
5
|
const role_based_token_verifier_1 = require("./role-based-token-verifier");
|
|
6
6
|
class JwksUriTokenVerifier extends role_based_token_verifier_1.RoleBasedTokenVerifier {
|
|
7
|
+
issuer;
|
|
8
|
+
jwksUri;
|
|
9
|
+
buildClient;
|
|
10
|
+
buildKeyResolver;
|
|
11
|
+
verifyToken;
|
|
7
12
|
constructor(issuer, jwksUri, rolesClaim, dependencies) {
|
|
8
|
-
var _a, _b, _c;
|
|
9
13
|
super(rolesClaim);
|
|
10
14
|
this.issuer = issuer;
|
|
11
15
|
this.jwksUri = jwksUri;
|
|
12
|
-
this.buildClient =
|
|
16
|
+
this.buildClient = dependencies?.jwksClientFactory ?? utilities_1.getJwksClient;
|
|
13
17
|
this.buildKeyResolver =
|
|
14
|
-
|
|
15
|
-
this.verifyToken =
|
|
18
|
+
dependencies?.keyResolverBuilder ?? ((client) => utilities_1.getKeyWithClient.bind(this, client));
|
|
19
|
+
this.verifyToken = dependencies?.jwtVerifier ?? utilities_1.verifyJWT;
|
|
16
20
|
}
|
|
17
21
|
async verify(token) {
|
|
18
22
|
const client = this.buildClient(this.jwksUri);
|
|
@@ -4,12 +4,14 @@ exports.PublicKeyTokenVerifier = void 0;
|
|
|
4
4
|
const utilities_1 = require("./utilities");
|
|
5
5
|
const role_based_token_verifier_1 = require("./role-based-token-verifier");
|
|
6
6
|
class PublicKeyTokenVerifier extends role_based_token_verifier_1.RoleBasedTokenVerifier {
|
|
7
|
+
issuer;
|
|
8
|
+
publicKeyResolver;
|
|
9
|
+
verifyToken;
|
|
7
10
|
constructor(issuer, publicKeyResolver, rolesClaim, dependencies) {
|
|
8
|
-
var _a;
|
|
9
11
|
super(rolesClaim);
|
|
10
12
|
this.issuer = issuer;
|
|
11
13
|
this.publicKeyResolver = publicKeyResolver;
|
|
12
|
-
this.verifyToken =
|
|
14
|
+
this.verifyToken = dependencies?.jwtVerifier ?? utilities_1.verifyJWT;
|
|
13
15
|
}
|
|
14
16
|
async verify(token) {
|
|
15
17
|
const key = await this.publicKeyResolver;
|
|
@@ -15,12 +15,13 @@ function rolesFromTokenRole(rolesClaim) {
|
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
class RoleBasedTokenVerifier {
|
|
18
|
+
rolesClaim;
|
|
18
19
|
constructor(rolesClaim = exports.DEFAULT_ROLES_CLAIM) {
|
|
19
20
|
this.rolesClaim = rolesClaim;
|
|
20
21
|
}
|
|
21
22
|
toUserEnvelope(decodedToken) {
|
|
22
23
|
const payload = decodedToken.payload;
|
|
23
|
-
const username =
|
|
24
|
+
const username = payload?.email || payload?.phone_number || payload.sub;
|
|
24
25
|
const id = payload.sub;
|
|
25
26
|
const roles = rolesFromTokenRole(payload[this.rolesClaim]);
|
|
26
27
|
return {
|
|
@@ -3,107 +3,114 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MagekSubscribersNotifier = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const common_1 = require("@magek/common");
|
|
6
|
+
const promises_1 = require("./utils/promises");
|
|
6
7
|
const graphql = require("graphql");
|
|
7
8
|
const graphql_generator_1 = require("./services/graphql/graphql-generator");
|
|
8
9
|
const read_model_pub_sub_1 = require("./services/pub-sub/read-model-pub-sub");
|
|
9
10
|
const instrumentation_1 = require("./instrumentation");
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const readModelEnvelopes = await this.config.readModelStore.rawToEnvelopes(this.config, request);
|
|
20
|
-
logger.debug('[SubsciptionDispatcher] The following ReadModels were updated: ', readModelEnvelopes);
|
|
21
|
-
const subscriptions = await this.getSubscriptions(readModelEnvelopes);
|
|
22
|
-
logger.debug('Found the following subscriptions for those read models: ', subscriptions);
|
|
23
|
-
const pubSub = this.getPubSub(readModelEnvelopes);
|
|
24
|
-
await common_1.Promises.allSettledAndFulfilled(subscriptions.map(this.runSubscriptionAndNotify.bind(this, pubSub)));
|
|
11
|
+
let MagekSubscribersNotifier = (() => {
|
|
12
|
+
let _instanceExtraInitializers = [];
|
|
13
|
+
let _dispatch_decorators;
|
|
14
|
+
return class MagekSubscribersNotifier {
|
|
15
|
+
static {
|
|
16
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
17
|
+
_dispatch_decorators = [(0, instrumentation_1.trace)(common_1.TraceActionTypes.DISPATCH_SUBSCRIBER_NOTIFIER)];
|
|
18
|
+
tslib_1.__esDecorate(this, null, _dispatch_decorators, { kind: "method", name: "dispatch", static: false, private: false, access: { has: obj => "dispatch" in obj, get: obj => obj.dispatch }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
19
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
25
20
|
}
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
config = tslib_1.__runInitializers(this, _instanceExtraInitializers);
|
|
22
|
+
graphQLSchema;
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.config = config;
|
|
25
|
+
this.graphQLSchema = graphql_generator_1.GraphQLGenerator.generateSchema(config);
|
|
28
26
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
async dispatch(request) {
|
|
28
|
+
const logger = (0, common_1.getLogger)(this.config, 'MagekSubscribersNotifier#dispatch');
|
|
29
|
+
try {
|
|
30
|
+
logger.debug('Received the following event for subscription dispatching: ', request);
|
|
31
|
+
const readModelEnvelopes = await this.config.readModelStore.rawToEnvelopes(this.config, request);
|
|
32
|
+
logger.debug('[SubsciptionDispatcher] The following ReadModels were updated: ', readModelEnvelopes);
|
|
33
|
+
const subscriptions = await this.getSubscriptions(readModelEnvelopes);
|
|
34
|
+
logger.debug('Found the following subscriptions for those read models: ', subscriptions);
|
|
35
|
+
const pubSub = this.getPubSub(readModelEnvelopes);
|
|
36
|
+
await promises_1.Promises.allSettledAndFulfilled(subscriptions.map(this.runSubscriptionAndNotify.bind(this, pubSub)));
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
logger.error(e);
|
|
40
|
+
}
|
|
34
41
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return subscriptionSets.flat();
|
|
44
|
-
}
|
|
45
|
-
getPubSub(readModelEnvelopes) {
|
|
46
|
-
const readModelInstances = readModelEnvelopes.map(this.getReadModelInstance, this);
|
|
47
|
-
return new read_model_pub_sub_1.FilteredReadModelPubSub(readModelInstances);
|
|
48
|
-
}
|
|
49
|
-
async runSubscriptionAndNotify(pubSub, subscription) {
|
|
50
|
-
const logger = (0, common_1.getLogger)(this.config, 'MagekSubscribersNotifier#runSubscriptionAndNotify');
|
|
51
|
-
const context = {
|
|
52
|
-
connectionID: subscription.connectionID,
|
|
53
|
-
responseHeaders: {},
|
|
54
|
-
requestID: subscription.requestID,
|
|
55
|
-
user: subscription.currentUser,
|
|
56
|
-
operation: subscription.operation,
|
|
57
|
-
pubSub,
|
|
58
|
-
storeSubscriptions: false, // We don't store the subscription again, just get the result now
|
|
59
|
-
};
|
|
60
|
-
const document = this.parseSubscriptionQuery(subscription.operation.query);
|
|
61
|
-
logger.debug('Running subscription with context: ', context);
|
|
62
|
-
const iterator = await graphql.subscribe({
|
|
63
|
-
contextValue: context,
|
|
64
|
-
document: document,
|
|
65
|
-
schema: this.graphQLSchema,
|
|
66
|
-
variableValues: subscription.operation.variables,
|
|
67
|
-
});
|
|
68
|
-
if ('next' in iterator) {
|
|
69
|
-
// It is an AsyncIterator
|
|
70
|
-
return this.processSubscriptionsIterator(iterator, subscription);
|
|
42
|
+
getReadModelInstance(envelope) {
|
|
43
|
+
const readModelMetadata = this.config.readModels[envelope.typeName];
|
|
44
|
+
if (!readModelMetadata) {
|
|
45
|
+
throw new Error('Could not get information about read model with name: ' + envelope.typeName);
|
|
46
|
+
}
|
|
47
|
+
const readModelInstance = new readModelMetadata.class();
|
|
48
|
+
Object.assign(readModelInstance, envelope.value);
|
|
49
|
+
return readModelInstance;
|
|
71
50
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// We probably don't need to validate this again, as it was validated before storing it. BUT! It's always better to fail early
|
|
78
|
-
const errors = graphql.validate(this.graphQLSchema, document);
|
|
79
|
-
if (errors.length > 0) {
|
|
80
|
-
throw errors;
|
|
51
|
+
async getSubscriptions(readModelEnvelopes) {
|
|
52
|
+
const readModelNames = readModelEnvelopes.map((readModelEnvelope) => readModelEnvelope.typeName);
|
|
53
|
+
const readModelUniqueNames = [...new Set(readModelNames)];
|
|
54
|
+
const subscriptionSets = await Promise.all(readModelUniqueNames.map((name) => this.config.sessionStore.fetchSubscriptionsByClassName(this.config, name)));
|
|
55
|
+
return subscriptionSets.flat();
|
|
81
56
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const notificationPromises = [];
|
|
86
|
-
for await (const result of iterator) {
|
|
87
|
-
notificationPromises.push(this.notifyWithGraphQLResult(subscription, result));
|
|
57
|
+
getPubSub(readModelEnvelopes) {
|
|
58
|
+
const readModelInstances = readModelEnvelopes.map(this.getReadModelInstance, this);
|
|
59
|
+
return new read_model_pub_sub_1.FilteredReadModelPubSub(readModelInstances);
|
|
88
60
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
61
|
+
async runSubscriptionAndNotify(pubSub, subscription) {
|
|
62
|
+
const logger = (0, common_1.getLogger)(this.config, 'MagekSubscribersNotifier#runSubscriptionAndNotify');
|
|
63
|
+
const context = {
|
|
64
|
+
connectionID: subscription.connectionID,
|
|
65
|
+
responseHeaders: {},
|
|
66
|
+
requestID: subscription.requestID,
|
|
67
|
+
user: subscription.currentUser,
|
|
68
|
+
operation: subscription.operation,
|
|
69
|
+
pubSub,
|
|
70
|
+
storeSubscriptions: false, // We don't store the subscription again, just get the result now
|
|
71
|
+
};
|
|
72
|
+
const document = this.parseSubscriptionQuery(subscription.operation.query);
|
|
73
|
+
logger.debug('Running subscription with context: ', context);
|
|
74
|
+
const iterator = await graphql.subscribe({
|
|
75
|
+
contextValue: context,
|
|
76
|
+
document: document,
|
|
77
|
+
schema: this.graphQLSchema,
|
|
78
|
+
variableValues: subscription.operation.variables,
|
|
79
|
+
});
|
|
80
|
+
if ('next' in iterator) {
|
|
81
|
+
// It is an AsyncIterator
|
|
82
|
+
return this.processSubscriptionsIterator(iterator, subscription);
|
|
83
|
+
}
|
|
84
|
+
// If "subscribe" returns an ExecutionResult (instead of an async iterator) the subscription failed.
|
|
85
|
+
throw iterator.errors;
|
|
95
86
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
87
|
+
parseSubscriptionQuery(query) {
|
|
88
|
+
const document = graphql.parse(query);
|
|
89
|
+
// We probably don't need to validate this again, as it was validated before storing it. BUT! It's always better to fail early
|
|
90
|
+
const errors = graphql.validate(this.graphQLSchema, document);
|
|
91
|
+
if (errors.length > 0) {
|
|
92
|
+
throw errors;
|
|
93
|
+
}
|
|
94
|
+
return document;
|
|
95
|
+
}
|
|
96
|
+
async processSubscriptionsIterator(iterator, subscription) {
|
|
97
|
+
const notificationPromises = [];
|
|
98
|
+
for await (const result of iterator) {
|
|
99
|
+
notificationPromises.push(this.notifyWithGraphQLResult(subscription, result));
|
|
100
|
+
}
|
|
101
|
+
return Promise.all(notificationPromises);
|
|
102
|
+
}
|
|
103
|
+
async notifyWithGraphQLResult(subscription, result) {
|
|
104
|
+
const logger = (0, common_1.getLogger)(this.config, 'MagekSubscribersNotifier#notifyWithGraphQLResult');
|
|
105
|
+
if (result.errors) {
|
|
106
|
+
throw result.errors;
|
|
107
|
+
}
|
|
108
|
+
const readModel = result.data;
|
|
109
|
+
const message = new common_1.GraphQLData(subscription.operation.id, { data: readModel });
|
|
110
|
+
logger.debug(`Notifying connectionID '${subscription.connectionID}' with the following wrappeed read model: `, readModel);
|
|
111
|
+
await this.config.runtime.messaging.sendMessage(this.config, subscription.connectionID, message);
|
|
112
|
+
logger.debug('Notifications sent');
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
})();
|
|
103
116
|
exports.MagekSubscribersNotifier = MagekSubscribersNotifier;
|
|
104
|
-
tslib_1.__decorate([
|
|
105
|
-
(0, instrumentation_1.Trace)(common_1.TraceActionTypes.DISPATCH_SUBSCRIBER_NOTIFIER),
|
|
106
|
-
tslib_1.__metadata("design:type", Function),
|
|
107
|
-
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
108
|
-
tslib_1.__metadata("design:returntype", Promise)
|
|
109
|
-
], MagekSubscribersNotifier.prototype, "dispatch", null);
|
package/dist/token-verifier.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.MagekTokenVerifier = void 0;
|
|
|
4
4
|
const common_1 = require("@magek/common");
|
|
5
5
|
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
6
6
|
class MagekTokenVerifier {
|
|
7
|
+
config;
|
|
7
8
|
constructor(config) {
|
|
8
9
|
this.config = config;
|
|
9
10
|
}
|
|
@@ -36,7 +37,7 @@ class MagekTokenVerifier {
|
|
|
36
37
|
}
|
|
37
38
|
getErrors(results) {
|
|
38
39
|
return results
|
|
39
|
-
.filter((result) =>
|
|
40
|
+
.filter((result) => result?.status && result?.status !== 'fulfilled')
|
|
40
41
|
.map((result) => result);
|
|
41
42
|
}
|
|
42
43
|
static joinReasons(errors) {
|