@magek/core 0.0.6 → 0.0.7
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 +50 -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 +3 -0
- package/dist/decorators/index.js +2 -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/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 +106 -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 +16 -17
- 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 +98 -92
- package/dist/token-verifier.js +2 -1
- 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}`;
|
|
@@ -6,6 +6,7 @@ const global_error_dispatcher_1 = require("../global-error-dispatcher");
|
|
|
6
6
|
const read_model_searcher_1 = require("./read-model-searcher");
|
|
7
7
|
const read_model_schema_migrator_1 = require("../read-model-schema-migrator");
|
|
8
8
|
class ReadModelStore {
|
|
9
|
+
config;
|
|
9
10
|
constructor(config) {
|
|
10
11
|
this.config = config;
|
|
11
12
|
}
|
|
@@ -82,7 +83,7 @@ class ReadModelStore {
|
|
|
82
83
|
getUnProjections(entitySnapshotEnvelope) {
|
|
83
84
|
const unProjections = this.entityUnProjections(entitySnapshotEnvelope);
|
|
84
85
|
const projections = this.entityProjections(entitySnapshotEnvelope);
|
|
85
|
-
if (
|
|
86
|
+
if (projections?.length > 0) {
|
|
86
87
|
if (!unProjections) {
|
|
87
88
|
throw new Error(`Missing UnProjections for entity ${entitySnapshotEnvelope.entityTypeName}`);
|
|
88
89
|
}
|
|
@@ -139,46 +140,45 @@ class ReadModelStore {
|
|
|
139
140
|
const entitiesJoinKeys = Array.isArray(entityJoinKey) ? entityJoinKey : [entityJoinKey];
|
|
140
141
|
// 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
142
|
for (const readModelId of entitiesJoinKeys) {
|
|
142
|
-
const readModel =
|
|
143
|
+
const readModel = currentReadModel?.id === readModelId ? currentReadModel : undefined;
|
|
143
144
|
projections.push(this.projectAndStoreReadModelWithRetry(entitySnapshotEnvelope, readModelName, sequenceKey, entityInstance, projectionMetadata, deleteEvent, readModelId, readModel));
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
else {
|
|
147
148
|
// 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
|
|
149
|
+
projections.push(this.projectAndStoreReadModelWithRetry(entitySnapshotEnvelope, readModelName, sequenceKey, entityInstance, projectionMetadata, deleteEvent, currentReadModel?.id, currentReadModel));
|
|
149
150
|
}
|
|
150
151
|
return projections;
|
|
151
152
|
}
|
|
152
153
|
async projectAndStoreReadModelWithRetry(entitySnapshotEnvelope, readModelName, sequenceKey, entityInstance, projectionMetadata, deleteEvent, readModelId, currentReadModel) {
|
|
153
154
|
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 ${
|
|
155
|
+
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
156
|
return (0, common_1.retryIfError)((tryNumber) => this.applyProjectionToReadModel(entitySnapshotEnvelope, entityInstance, projectionMetadata, deleteEvent, currentReadModel, entitySnapshotEnvelope, readModelId, sequenceKey, tryNumber), common_1.OptimisticConcurrencyUnexpectedVersionError, logger);
|
|
156
157
|
}
|
|
157
158
|
instanceReadModels(readModelName, rawReadModels) {
|
|
158
|
-
if (!
|
|
159
|
+
if (!rawReadModels?.length) {
|
|
159
160
|
return [];
|
|
160
161
|
}
|
|
161
162
|
const readModelMetadata = this.config.readModels[readModelName];
|
|
162
163
|
return rawReadModels.map((rawReadModel) => (0, common_1.createInstance)(readModelMetadata.class, rawReadModel));
|
|
163
164
|
}
|
|
164
165
|
async applyProjectionToReadModel(entitySnapshotEnvelope, entity, projectionMetadata, deleteEvent, currentReadModel, lastProjectedEntity, currentReadModelID, sequenceKey, tryNumber) {
|
|
165
|
-
var _a, _b, _c, _d, _e, _f;
|
|
166
166
|
const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#applyProjectionToReadModel');
|
|
167
167
|
const readModelName = projectionMetadata.class.name;
|
|
168
|
-
const readModelID = currentReadModelID
|
|
168
|
+
const readModelID = currentReadModelID ?? currentReadModel?.id;
|
|
169
169
|
if (tryNumber && tryNumber > 1) {
|
|
170
170
|
// In case of optimistic concurrency error, we need to fetch the current read model version and retry
|
|
171
|
-
logger.debug(`OptimisticConcurrencyUnexpectedVersionError (version=${
|
|
171
|
+
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
172
|
(sequenceKey ? ` and sequence key ${sequenceKey.name} = ${sequenceKey.value}` : ''));
|
|
173
173
|
currentReadModel = await this.fetchReadModel(readModelName, readModelID, sequenceKey);
|
|
174
|
-
logger.debug(`Current read model ${readModelName} with ID ${readModelID} updated with version = ${
|
|
174
|
+
logger.debug(`Current read model ${readModelName} with ID ${readModelID} updated with version = ${currentReadModel?.magekMetadata?.version}` +
|
|
175
175
|
(sequenceKey ? ` and sequence key ${sequenceKey.name} = ${sequenceKey.value}` : ''));
|
|
176
176
|
}
|
|
177
177
|
let migratedReadModel;
|
|
178
178
|
if (currentReadModel) {
|
|
179
179
|
migratedReadModel = await new read_model_schema_migrator_1.ReadModelSchemaMigrator(this.config).migrate(currentReadModel, readModelName);
|
|
180
180
|
}
|
|
181
|
-
const currentDatabaseVersion =
|
|
181
|
+
const currentDatabaseVersion = migratedReadModel?.magekMetadata?.version ?? 0;
|
|
182
182
|
let newReadModel;
|
|
183
183
|
const projectionInfo = {
|
|
184
184
|
reason: deleteEvent ? common_1.ProjectionInfoReason.ENTITY_DELETED : common_1.ProjectionInfoReason.ENTITY_PROJECTED,
|
|
@@ -192,31 +192,30 @@ class ReadModelStore {
|
|
|
192
192
|
if (error)
|
|
193
193
|
throw error;
|
|
194
194
|
}
|
|
195
|
-
if (newReadModel === common_1.
|
|
195
|
+
if (newReadModel === common_1.ProjectionAction.Delete) {
|
|
196
196
|
logger.debug(`Deleting read model ${readModelName} with ID ${readModelID}:`, migratedReadModel);
|
|
197
197
|
return this.config.readModelStore.delete(this.config, readModelName, readModelID);
|
|
198
198
|
}
|
|
199
|
-
else if (newReadModel === common_1.
|
|
199
|
+
else if (newReadModel === common_1.ProjectionAction.Skip) {
|
|
200
200
|
logger.debug(`Skipping actions for ${readModelName} with ID ${readModelID}:`, newReadModel);
|
|
201
201
|
return;
|
|
202
202
|
}
|
|
203
203
|
return await this.store(entity, projectionMetadata, readModelID, readModelName, migratedReadModel, newReadModel, currentDatabaseVersion, lastProjectedEntity);
|
|
204
204
|
}
|
|
205
205
|
async store(entity, projectionMetadata, readModelID, readModelName, migratedReadModel, newReadModel, expectedCurrentDatabaseVersion, lastProjectedEntity) {
|
|
206
|
-
var _a, _b;
|
|
207
206
|
const logger = (0, common_1.getLogger)(this.config, 'ReadModelStore#store');
|
|
208
|
-
const schemaVersion =
|
|
207
|
+
const schemaVersion = migratedReadModel?.magekMetadata?.schemaVersion ?? this.config.currentVersionFor(readModelName);
|
|
209
208
|
// Increment the read model version in 1 before storing
|
|
210
209
|
const newReadModelVersion = expectedCurrentDatabaseVersion + 1;
|
|
211
210
|
newReadModel.magekMetadata = {
|
|
212
|
-
...migratedReadModel
|
|
211
|
+
...migratedReadModel?.magekMetadata,
|
|
213
212
|
version: newReadModelVersion,
|
|
214
213
|
schemaVersion: schemaVersion,
|
|
215
214
|
lastUpdateAt: new Date().toISOString(),
|
|
216
215
|
lastProjectionInfo: {
|
|
217
216
|
entityId: entity.id,
|
|
218
|
-
entityName: lastProjectedEntity
|
|
219
|
-
entityUpdatedAt: lastProjectedEntity
|
|
217
|
+
entityName: lastProjectedEntity?.entityTypeName,
|
|
218
|
+
entityUpdatedAt: lastProjectedEntity?.createdAt,
|
|
220
219
|
projectionMethod: `${projectionMetadata.class.name}.${projectionMetadata.methodName}`,
|
|
221
220
|
},
|
|
222
221
|
};
|
|
@@ -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 {
|
|
@@ -7,103 +7,109 @@ const graphql = require("graphql");
|
|
|
7
7
|
const graphql_generator_1 = require("./services/graphql/graphql-generator");
|
|
8
8
|
const read_model_pub_sub_1 = require("./services/pub-sub/read-model-pub-sub");
|
|
9
9
|
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)));
|
|
10
|
+
let MagekSubscribersNotifier = (() => {
|
|
11
|
+
let _instanceExtraInitializers = [];
|
|
12
|
+
let _dispatch_decorators;
|
|
13
|
+
return class MagekSubscribersNotifier {
|
|
14
|
+
static {
|
|
15
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
16
|
+
_dispatch_decorators = [(0, instrumentation_1.trace)(common_1.TraceActionTypes.DISPATCH_SUBSCRIBER_NOTIFIER)];
|
|
17
|
+
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);
|
|
18
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
25
19
|
}
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
config = tslib_1.__runInitializers(this, _instanceExtraInitializers);
|
|
21
|
+
graphQLSchema;
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.config = config;
|
|
24
|
+
this.graphQLSchema = graphql_generator_1.GraphQLGenerator.generateSchema(config);
|
|
28
25
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
async dispatch(request) {
|
|
27
|
+
const logger = (0, common_1.getLogger)(this.config, 'MagekSubscribersNotifier#dispatch');
|
|
28
|
+
try {
|
|
29
|
+
logger.debug('Received the following event for subscription dispatching: ', request);
|
|
30
|
+
const readModelEnvelopes = await this.config.readModelStore.rawToEnvelopes(this.config, request);
|
|
31
|
+
logger.debug('[SubsciptionDispatcher] The following ReadModels were updated: ', readModelEnvelopes);
|
|
32
|
+
const subscriptions = await this.getSubscriptions(readModelEnvelopes);
|
|
33
|
+
logger.debug('Found the following subscriptions for those read models: ', subscriptions);
|
|
34
|
+
const pubSub = this.getPubSub(readModelEnvelopes);
|
|
35
|
+
await common_1.Promises.allSettledAndFulfilled(subscriptions.map(this.runSubscriptionAndNotify.bind(this, pubSub)));
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
logger.error(e);
|
|
39
|
+
}
|
|
34
40
|
}
|
|
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);
|
|
41
|
+
getReadModelInstance(envelope) {
|
|
42
|
+
const readModelMetadata = this.config.readModels[envelope.typeName];
|
|
43
|
+
if (!readModelMetadata) {
|
|
44
|
+
throw new Error('Could not get information about read model with name: ' + envelope.typeName);
|
|
45
|
+
}
|
|
46
|
+
const readModelInstance = new readModelMetadata.class();
|
|
47
|
+
Object.assign(readModelInstance, envelope.value);
|
|
48
|
+
return readModelInstance;
|
|
71
49
|
}
|
|
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;
|
|
50
|
+
async getSubscriptions(readModelEnvelopes) {
|
|
51
|
+
const readModelNames = readModelEnvelopes.map((readModelEnvelope) => readModelEnvelope.typeName);
|
|
52
|
+
const readModelUniqueNames = [...new Set(readModelNames)];
|
|
53
|
+
const subscriptionSets = await Promise.all(readModelUniqueNames.map((name) => this.config.sessionStore.fetchSubscriptionsByClassName(this.config, name)));
|
|
54
|
+
return subscriptionSets.flat();
|
|
81
55
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const notificationPromises = [];
|
|
86
|
-
for await (const result of iterator) {
|
|
87
|
-
notificationPromises.push(this.notifyWithGraphQLResult(subscription, result));
|
|
56
|
+
getPubSub(readModelEnvelopes) {
|
|
57
|
+
const readModelInstances = readModelEnvelopes.map(this.getReadModelInstance, this);
|
|
58
|
+
return new read_model_pub_sub_1.FilteredReadModelPubSub(readModelInstances);
|
|
88
59
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
60
|
+
async runSubscriptionAndNotify(pubSub, subscription) {
|
|
61
|
+
const logger = (0, common_1.getLogger)(this.config, 'MagekSubscribersNotifier#runSubscriptionAndNotify');
|
|
62
|
+
const context = {
|
|
63
|
+
connectionID: subscription.connectionID,
|
|
64
|
+
responseHeaders: {},
|
|
65
|
+
requestID: subscription.requestID,
|
|
66
|
+
user: subscription.currentUser,
|
|
67
|
+
operation: subscription.operation,
|
|
68
|
+
pubSub,
|
|
69
|
+
storeSubscriptions: false, // We don't store the subscription again, just get the result now
|
|
70
|
+
};
|
|
71
|
+
const document = this.parseSubscriptionQuery(subscription.operation.query);
|
|
72
|
+
logger.debug('Running subscription with context: ', context);
|
|
73
|
+
const iterator = await graphql.subscribe({
|
|
74
|
+
contextValue: context,
|
|
75
|
+
document: document,
|
|
76
|
+
schema: this.graphQLSchema,
|
|
77
|
+
variableValues: subscription.operation.variables,
|
|
78
|
+
});
|
|
79
|
+
if ('next' in iterator) {
|
|
80
|
+
// It is an AsyncIterator
|
|
81
|
+
return this.processSubscriptionsIterator(iterator, subscription);
|
|
82
|
+
}
|
|
83
|
+
// If "subscribe" returns an ExecutionResult (instead of an async iterator) the subscription failed.
|
|
84
|
+
throw iterator.errors;
|
|
95
85
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
86
|
+
parseSubscriptionQuery(query) {
|
|
87
|
+
const document = graphql.parse(query);
|
|
88
|
+
// We probably don't need to validate this again, as it was validated before storing it. BUT! It's always better to fail early
|
|
89
|
+
const errors = graphql.validate(this.graphQLSchema, document);
|
|
90
|
+
if (errors.length > 0) {
|
|
91
|
+
throw errors;
|
|
92
|
+
}
|
|
93
|
+
return document;
|
|
94
|
+
}
|
|
95
|
+
async processSubscriptionsIterator(iterator, subscription) {
|
|
96
|
+
const notificationPromises = [];
|
|
97
|
+
for await (const result of iterator) {
|
|
98
|
+
notificationPromises.push(this.notifyWithGraphQLResult(subscription, result));
|
|
99
|
+
}
|
|
100
|
+
return Promise.all(notificationPromises);
|
|
101
|
+
}
|
|
102
|
+
async notifyWithGraphQLResult(subscription, result) {
|
|
103
|
+
const logger = (0, common_1.getLogger)(this.config, 'MagekSubscribersNotifier#notifyWithGraphQLResult');
|
|
104
|
+
if (result.errors) {
|
|
105
|
+
throw result.errors;
|
|
106
|
+
}
|
|
107
|
+
const readModel = result.data;
|
|
108
|
+
const message = new common_1.GraphQLData(subscription.operation.id, { data: readModel });
|
|
109
|
+
logger.debug(`Notifying connectionID '${subscription.connectionID}' with the following wrappeed read model: `, readModel);
|
|
110
|
+
await this.config.runtime.messaging.sendMessage(this.config, subscription.connectionID, message);
|
|
111
|
+
logger.debug('Notifications sent');
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
})();
|
|
103
115
|
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) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magek/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Library for your Magek apps",
|
|
5
5
|
"author": "Boosterin Labs SLU",
|
|
6
6
|
"homepage": "https://magek.ai",
|
|
@@ -23,19 +23,19 @@
|
|
|
23
23
|
"node": ">=22.0.0 <23.0.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@magek/common": "^0.0.
|
|
26
|
+
"@magek/common": "^0.0.7",
|
|
27
27
|
"fp-ts": "2.16.11",
|
|
28
28
|
"graphql-scalars": "1.25.0",
|
|
29
29
|
"inflected": "2.1.0",
|
|
30
30
|
"iterall": "1.3.0",
|
|
31
31
|
"jsonwebtoken": "9.0.3",
|
|
32
|
-
"jwks-rsa": "3.2.
|
|
32
|
+
"jwks-rsa": "3.2.2",
|
|
33
33
|
"tslib": "2.8.1",
|
|
34
34
|
"validator": "13.15.26",
|
|
35
35
|
"fast-check": "4.5.3"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@magek/eslint-config": "^0.0.
|
|
38
|
+
"@magek/eslint-config": "^0.0.7",
|
|
39
39
|
"@types/chai": "5.2.3",
|
|
40
40
|
"@types/chai-as-promised": "8.0.2",
|
|
41
41
|
"@types/inflected": "2.1.3",
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Transfer field metadata from Stage 3 decorator context.metadata to the class constructor.
|
|
3
|
-
* This is needed because Symbol.metadata is not available in Node.js, so Stage 3 decorators
|
|
4
|
-
* need an explicit step to make field metadata accessible on the class.
|
|
5
|
-
*/
|
|
6
|
-
export declare function transferStage3FieldMetadata(classType: Function, contextMetadata: Record<string | symbol, unknown>): void;
|