@monorise/core 1.0.2 → 1.0.4-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/mock/monorise/chapter.d.ts +1 -1
  2. package/dist/mock/monorise/course.d.ts +1 -1
  3. package/dist/mock/monorise/learning-journey-config.d.ts +1 -1
  4. package/dist/mock/monorise/module.d.ts +1 -1
  5. package/dist/mock/monorise/organization.d.ts +1 -1
  6. package/dist/services/entity.service.d.ts +2 -2
  7. package/dist/services/entity.service.d.ts.map +1 -1
  8. package/package.json +4 -1
  9. package/configs/service.config.ts +0 -14
  10. package/constants/table.ts +0 -3
  11. package/controllers/entity/create-entity.controller.ts +0 -51
  12. package/controllers/entity/delete-entity.controller.ts +0 -35
  13. package/controllers/entity/entity.http +0 -62
  14. package/controllers/entity/get-entity.controller.ts +0 -33
  15. package/controllers/entity/list-entities.controller.ts +0 -69
  16. package/controllers/entity/update-entity.controller.ts +0 -56
  17. package/controllers/entity/upsert-entity.controller.ts +0 -97
  18. package/controllers/mutual/create-mutual.controller.ts +0 -89
  19. package/controllers/mutual/delete-mutual.controller.ts +0 -40
  20. package/controllers/mutual/get-mutual.controller.ts +0 -38
  21. package/controllers/mutual/list-entities-by-entity.controller.ts +0 -76
  22. package/controllers/mutual/mutual.http +0 -88
  23. package/controllers/mutual/update-mutual.controller.ts +0 -50
  24. package/controllers/setupRoutes.ts +0 -73
  25. package/controllers/tag/list-tags.controller.ts +0 -57
  26. package/data/DbUtils.ts +0 -40
  27. package/data/Entity.ts +0 -499
  28. package/data/EventUtils.ts +0 -47
  29. package/data/FileObject.ts +0 -16
  30. package/data/Mutual.ts +0 -779
  31. package/data/ProjectionExpression.ts +0 -8
  32. package/data/Tag.ts +0 -470
  33. package/data/abstract/Item.base.ts +0 -19
  34. package/data/abstract/Repository.base.ts +0 -92
  35. package/errors/api-error.ts +0 -39
  36. package/errors/extendable-error.ts +0 -35
  37. package/errors/standard-error.ts +0 -29
  38. package/helpers/dependencies.ts +0 -10
  39. package/helpers/event.ts +0 -85
  40. package/helpers/fromLastKeyQuery.ts +0 -11
  41. package/helpers/sleep.ts +0 -1
  42. package/helpers/toLastKeyResponse.ts +0 -11
  43. package/index.ts +0 -23
  44. package/middlewares/entity-type-check.ts +0 -20
  45. package/middlewares/mutual-type-check.ts +0 -26
  46. package/mock/entity.ts +0 -12
  47. package/mock/monorise/admin.ts +0 -35
  48. package/mock/monorise/chapter.ts +0 -94
  49. package/mock/monorise/course.ts +0 -149
  50. package/mock/monorise/index.ts +0 -143
  51. package/mock/monorise/learner.ts +0 -66
  52. package/mock/monorise/learning-activity.ts +0 -62
  53. package/mock/monorise/learning-journey-config.ts +0 -34
  54. package/mock/monorise/module.ts +0 -108
  55. package/mock/monorise/organization.ts +0 -63
  56. package/mock/monorise/reference.ts +0 -28
  57. package/mock/monorise/video.ts +0 -36
  58. package/processors/create-entity-processor.ts +0 -55
  59. package/processors/mutual-processor.ts +0 -262
  60. package/processors/prejoin-processor.ts +0 -264
  61. package/processors/replication-processor.ts +0 -261
  62. package/processors/tag-processor.ts +0 -174
  63. package/services/DependencyContainer.ts +0 -208
  64. package/services/entity-service-lifecycle.ts +0 -41
  65. package/services/entity.service.ts +0 -201
  66. package/services/mutual.service.ts +0 -285
  67. package/tsconfig.json +0 -116
  68. package/types/entity.type.ts +0 -62
  69. package/types/event.ts +0 -84
@@ -1,264 +0,0 @@
1
- import type { Entity } from '@monorise/base';
2
- import type { SQSBatchItemFailure, SQSEvent } from 'aws-lambda';
3
- import { AllowedEntityTypes, EntityConfig } from '#/lambda-layer/monorise';
4
- import type { MutualRepository } from '../data/Mutual';
5
- import { PROJECTION_EXPRESSION } from '../data/ProjectionExpression';
6
- import { parseSQSBusEvent } from '../helpers/event';
7
- import type { publishEvent as publishEventType } from '../helpers/event';
8
- import type { EventDetailBody as MutualProcessorEventDetailBody } from '../processors/mutual-processor';
9
- import { DependencyContainer } from '../services/DependencyContainer';
10
- import type { Prejoins } from '../types/entity.type';
11
- import { EVENT } from '../types/event';
12
-
13
- export type EventDetailBody = {
14
- byEntityType: Entity;
15
- byEntityId: string;
16
- entityType: Entity;
17
- publishedAt: string;
18
- };
19
-
20
- const container = new DependencyContainer();
21
-
22
- async function processPrejoins({
23
- mutualRepository,
24
- publishEvent,
25
- byEntityType,
26
- byEntityId,
27
- prejoins,
28
- publishedAt,
29
- }: {
30
- mutualRepository: MutualRepository;
31
- publishEvent: typeof publishEventType;
32
- byEntityType: Entity;
33
- byEntityId: string;
34
- prejoins: Prejoins;
35
- publishedAt: string;
36
- }) {
37
- const mutualCache: Partial<
38
- Record<
39
- Entity,
40
- Array<{
41
- byEntityType?: Entity;
42
- byEntityId?: string;
43
- entityType: Entity;
44
- entityId: string;
45
- }>
46
- >
47
- > = {
48
- /*
49
- course: [{
50
- byEntityType: 'module',
51
- byEntityId: '1',
52
- entityType: 'course',
53
- entityId: '1',
54
- }],
55
- module: [],
56
- chapter: [],
57
- video: []
58
- */
59
- };
60
-
61
- //initiate
62
- mutualCache[byEntityType] = [
63
- {
64
- entityType: byEntityType,
65
- entityId: byEntityId,
66
- },
67
- ];
68
-
69
- for (const { mutualField, targetEntityType, entityPaths } of prejoins) {
70
- let toBePublishedContext: Record<string, any> = {};
71
-
72
- for (const [index, entityPath] of entityPaths.entries()) {
73
- const entityType = entityPath.entityType as keyof typeof mutualCache;
74
-
75
- // skip cached
76
- if (!entityPath.skipCache && mutualCache[entityType]) {
77
- continue;
78
- }
79
-
80
- // if skipping cache should not have previous run data
81
- mutualCache[entityType] = [];
82
-
83
- const parentEntityType = entityPaths[index - 1]
84
- .entityType as keyof typeof mutualCache;
85
- const parentEntities = mutualCache[parentEntityType] ?? [];
86
-
87
- // find all nested entities
88
- for (const parentEntity of parentEntities) {
89
- const { entityType: parentEntityType, entityId: parentEntityId } =
90
- parentEntity;
91
-
92
- const { items: mutualItems } =
93
- await mutualRepository.listEntitiesByEntity(
94
- parentEntityType,
95
- parentEntityId,
96
- entityPath.entityType,
97
- {
98
- ProjectionExpression: PROJECTION_EXPRESSION.MUTUAL_DATA_ONLY,
99
- },
100
- );
101
-
102
- // custom processor defined in prejoin config for each path
103
- const processor =
104
- entityPath.processor || ((items, context) => ({ items, context }));
105
-
106
- const processed = processor(mutualItems, toBePublishedContext);
107
- toBePublishedContext = processed?.context || toBePublishedContext;
108
-
109
- mutualCache[entityType] = [
110
- ...(mutualCache[entityType] ?? []),
111
- ...(processed ? processed.items : mutualItems),
112
- ];
113
- }
114
-
115
- if (!mutualCache[entityType] && !Array.isArray(mutualCache[entityType])) {
116
- // to avoid empty array
117
- mutualCache[entityType] = [];
118
- }
119
- }
120
-
121
- const mutualIds = (
122
- mutualCache[targetEntityType as keyof typeof mutualCache] ?? []
123
- ).map((item) => item.entityId);
124
-
125
- await publishEvent<MutualProcessorEventDetailBody>({
126
- event: EVENT.CORE.ENTITY_MUTUAL_TO_UPDATE,
127
- payload: {
128
- byEntityType,
129
- byEntityId,
130
- entityType: targetEntityType,
131
- field: mutualField,
132
- mutualIds,
133
- customContext: toBePublishedContext,
134
- publishedAt,
135
- },
136
- });
137
- }
138
- }
139
-
140
- async function publishToSubscribers({
141
- mutualRepository,
142
- publishEvent,
143
- byEntityType,
144
- byEntityId,
145
- publishedAt,
146
- }: {
147
- mutualRepository: MutualRepository;
148
- publishEvent: typeof publishEventType;
149
- byEntityType: Entity;
150
- byEntityId: string;
151
- publishedAt: string;
152
- }) {
153
- const listeners = AllowedEntityTypes.reduce(
154
- (acc, configKey) => {
155
- const { subscribes } = EntityConfig[configKey].mutual ?? {};
156
-
157
- const hasSubscription = (subscribes ?? []).some(
158
- ({ entityType }) => entityType === byEntityType,
159
- );
160
-
161
- return [
162
- ...acc,
163
- ...(hasSubscription
164
- ? [
165
- {
166
- entityType: configKey,
167
- },
168
- ]
169
- : []),
170
- ];
171
- },
172
- [] as { entityType: Entity }[],
173
- );
174
-
175
- // publish event for each interested entity
176
- const subscribedMutualItems = await Promise.all(
177
- listeners.map(({ entityType: subscribedEntityType }) =>
178
- mutualRepository.listEntitiesByEntity(
179
- byEntityType,
180
- byEntityId,
181
- subscribedEntityType,
182
- {
183
- ProjectionExpression: PROJECTION_EXPRESSION.NO_DATA,
184
- },
185
- ),
186
- ),
187
- );
188
- const subscribedMutuals = subscribedMutualItems.flatMap((item) => item.items);
189
-
190
- await Promise.all(
191
- subscribedMutuals.map((subscribedMutual) =>
192
- publishEvent({
193
- event: EVENT.CORE.PREJOIN_RELATIONSHIP_SYNC,
194
- payload: {
195
- byEntityType: subscribedMutual.entityType,
196
- byEntityId: subscribedMutual.entityId,
197
- entityType: subscribedMutual.byEntityType,
198
- publishedAt,
199
- },
200
- }),
201
- ),
202
- );
203
- }
204
-
205
- export const handler = async (ev: SQSEvent) => {
206
- const batchItemFailures: SQSBatchItemFailure[] = [];
207
-
208
- const { mutualRepository, publishEvent } = container;
209
-
210
- for (const record of ev.Records) {
211
- const body = parseSQSBusEvent<EventDetailBody>(record.body);
212
- const { detail } = body;
213
- const { byEntityType, byEntityId, entityType, publishedAt } = detail;
214
- let errorContext: Record<string, unknown> = {
215
- body,
216
- };
217
-
218
- try {
219
- const isEntityTypeSubscribed = (
220
- EntityConfig[byEntityType]?.mutual?.subscribes ?? []
221
- ).some(
222
- ({ entityType: subscribedEntityType }) =>
223
- subscribedEntityType === entityType,
224
- );
225
- const hasPrejoins = EntityConfig[byEntityType]?.mutual?.prejoins;
226
- const shouldProcessPrejoins = isEntityTypeSubscribed && hasPrejoins;
227
- errorContext = {
228
- ...errorContext,
229
- isEntityTypeSubscribed,
230
- hasPrejoins,
231
- shouldProcessPrejoins,
232
- };
233
-
234
- if (shouldProcessPrejoins) {
235
- await processPrejoins({
236
- mutualRepository,
237
- publishEvent,
238
- byEntityType,
239
- byEntityId,
240
- prejoins: EntityConfig[byEntityType]?.mutual?.prejoins ?? [],
241
- publishedAt,
242
- });
243
- }
244
-
245
- await publishToSubscribers({
246
- mutualRepository,
247
- publishEvent,
248
- byEntityType,
249
- byEntityId,
250
- publishedAt,
251
- });
252
- } catch (err) {
253
- console.log(
254
- '===PREJOIN-PROCESSOR ERROR===',
255
- err,
256
- JSON.stringify({ errorContext }, null, 2),
257
- );
258
-
259
- batchItemFailures.push({ itemIdentifier: record.messageId });
260
- }
261
- }
262
-
263
- return { batchItemFailures };
264
- };
@@ -1,261 +0,0 @@
1
- import { ConditionalCheckFailedException } from '@aws-sdk/client-dynamodb';
2
- import type {
3
- AttributeValue,
4
- _Record as DynamoDBStreamEvent,
5
- } from '@aws-sdk/client-dynamodb-streams';
6
- import type { DynamoDBBatchItemFailure } from 'aws-lambda';
7
- import {
8
- ENTITY_REPLICATION_INDEX,
9
- MUTUAL_REPLICATION_INDEX,
10
- } from '../configs/service.config';
11
- import { StandardError } from '../errors/standard-error';
12
- import { DependencyContainer } from '../services/DependencyContainer';
13
-
14
- const container = new DependencyContainer();
15
- const TableName = process.env.DDB_TABLE;
16
-
17
- export const handler = async (event: { Records: DynamoDBStreamEvent[] }) => {
18
- const batchItemFailures: DynamoDBBatchItemFailure[] = [];
19
- const { dynamodbClient } = container;
20
-
21
- for (const record of event.Records) {
22
- const errorContext: any = {};
23
- errorContext.record = record;
24
-
25
- try {
26
- if (record.eventName === 'MODIFY') {
27
- const modifiedItem = record.dynamodb?.NewImage;
28
- if (!modifiedItem) {
29
- continue;
30
- }
31
-
32
- const isMetadata = modifiedItem.SK.S?.startsWith('#METADATA#');
33
- const isMutual = modifiedItem.PK.S?.startsWith('MUTUAL#') && isMetadata;
34
- const isEntity = isMetadata && !isMutual;
35
-
36
- if (!isEntity && !isMutual) {
37
- // skip replicated data
38
- continue;
39
- }
40
-
41
- // default variables
42
- let targetRPK = 'R1PK';
43
- const targetData = 'data';
44
- let targetIndexName = ENTITY_REPLICATION_INDEX;
45
- const targetUpdatedAt = 'updatedAt';
46
-
47
- let queryExpression: {
48
- FilterExpression?: string;
49
- ExpressionAttributeNames: Record<string, string>;
50
- ExpressionAttributeValues: Record<string, AttributeValue>;
51
- } = {
52
- FilterExpression: `#${targetUpdatedAt} < :${targetUpdatedAt}`,
53
- ExpressionAttributeNames: {
54
- [`#${targetRPK}`]: targetRPK,
55
- [`#${targetUpdatedAt}`]: targetUpdatedAt,
56
- },
57
- ExpressionAttributeValues: {
58
- [`:${targetRPK}`]: modifiedItem.PK,
59
- [`:${targetUpdatedAt}`]: modifiedItem.updatedAt,
60
- },
61
- };
62
-
63
- const updateExpession: {
64
- ConditionExpression: string;
65
- UpdateExpression: string;
66
- ExpressionAttributeNames: Record<string, string>;
67
- ExpressionAttributeValues: Record<string, AttributeValue>;
68
- } = {
69
- UpdateExpression: `SET #${targetUpdatedAt} = :${targetUpdatedAt}, #${targetData} = :${targetData}`,
70
- ConditionExpression: `#${targetUpdatedAt} < :${targetUpdatedAt}`,
71
- ExpressionAttributeNames: {
72
- [`#${targetData}`]: targetData,
73
- [`#${targetUpdatedAt}`]: targetUpdatedAt,
74
- },
75
- ExpressionAttributeValues: {
76
- [`:${targetData}`]: modifiedItem.data,
77
- [`:${targetUpdatedAt}`]: modifiedItem.updatedAt,
78
- },
79
- };
80
-
81
- if (isMutual) {
82
- targetRPK = 'R2PK';
83
- targetIndexName = MUTUAL_REPLICATION_INDEX;
84
-
85
- // condition to only replicate to mutualAsEntity
86
- queryExpression = {
87
- FilterExpression: `${queryExpression.FilterExpression} AND #SK = :metadata`, // to replicate to mutualAsEntity only
88
- ExpressionAttributeNames: {
89
- '#SK': 'SK',
90
- [`#${targetRPK}`]: targetRPK,
91
- [`#${targetUpdatedAt}`]: targetUpdatedAt,
92
- },
93
- ExpressionAttributeValues: {
94
- ':metadata': { S: '#METADATA#' },
95
- [`:${targetRPK}`]: modifiedItem.PK,
96
- [`:${targetUpdatedAt}`]: modifiedItem.mutualUpdatedAt,
97
- },
98
- };
99
-
100
- updateExpession.ExpressionAttributeValues = {
101
- [`:${targetData}`]: modifiedItem.mutualData,
102
- [`:${targetUpdatedAt}`]: modifiedItem.mutualUpdatedAt,
103
- };
104
- }
105
-
106
- errorContext.queryExpression = queryExpression;
107
- errorContext.updateExpession = updateExpession;
108
-
109
- // retrieve all to be replicated items
110
- let toBeReplicatedItems: Record<string, AttributeValue>[] = [];
111
- let lastKey;
112
-
113
- do {
114
- const queryResult = await dynamodbClient.query({
115
- TableName,
116
- IndexName: targetIndexName,
117
- KeyConditionExpression: `#${targetRPK} = :${targetRPK}`,
118
- ...queryExpression,
119
- });
120
-
121
- toBeReplicatedItems = [
122
- ...toBeReplicatedItems,
123
- ...(queryResult.Items || []),
124
- ];
125
- lastKey = queryResult.LastEvaluatedKey;
126
- } while (lastKey);
127
- errorContext.toBeReplicatedItems = toBeReplicatedItems;
128
-
129
- const updatePromises = toBeReplicatedItems.map((item) => {
130
- const updateParams = {
131
- TableName,
132
- Key: {
133
- PK: item.PK,
134
- SK: item.SK,
135
- },
136
- ...updateExpession,
137
- };
138
-
139
- return dynamodbClient.updateItem(updateParams);
140
- });
141
-
142
- const results = await Promise.allSettled(updatePromises);
143
- errorContext.results = results;
144
-
145
- if (
146
- results.some(
147
- (result) =>
148
- result.status === 'rejected' &&
149
- !(result.reason instanceof ConditionalCheckFailedException),
150
- )
151
- ) {
152
- throw new StandardError(
153
- 'REPLICATION_ERROR',
154
- 'Replication error',
155
- null,
156
- errorContext,
157
- );
158
- }
159
- }
160
-
161
- if (record.eventName === 'REMOVE') {
162
- const removedKeys = record.dynamodb?.Keys || {};
163
- const isMetadata = removedKeys.SK.S?.startsWith('#METADATA#');
164
- const isMutual = removedKeys.PK.S?.startsWith('MUTUAL#') && isMetadata;
165
- const isEntity = isMetadata && !isMutual;
166
-
167
- if (!isEntity && !isMutual) {
168
- continue;
169
- }
170
-
171
- // default query settings
172
- let targetRPK = 'R1PK';
173
- let targetIndexName: string = ENTITY_REPLICATION_INDEX;
174
-
175
- if (isMutual) {
176
- targetRPK = 'R2PK';
177
- targetIndexName = MUTUAL_REPLICATION_INDEX;
178
- }
179
-
180
- let itemsToDelete: Record<string, AttributeValue>[] = [];
181
- let lastKey;
182
-
183
- do {
184
- const queryResult = await dynamodbClient.query({
185
- TableName,
186
- IndexName: targetIndexName,
187
- KeyConditionExpression: `#${targetRPK} = :${targetRPK}`,
188
- ExpressionAttributeNames: {
189
- [`#${targetRPK}`]: targetRPK,
190
- },
191
- ExpressionAttributeValues: {
192
- [`:${targetRPK}`]: removedKeys.PK,
193
- },
194
- });
195
-
196
- itemsToDelete = [...itemsToDelete, ...(queryResult.Items || [])];
197
- lastKey = queryResult.LastEvaluatedKey;
198
- } while (lastKey);
199
-
200
- const mutualsToDelete = Array.from(
201
- new Set(
202
- itemsToDelete
203
- .filter((item) => item.R2PK?.S?.startsWith('MUTUAL#'))
204
- .map((filteredItem) => filteredItem.R2PK?.S),
205
- ),
206
- );
207
- mutualsToDelete.forEach((mutual) => {
208
- if (!mutual) {
209
- return;
210
- }
211
-
212
- itemsToDelete.push({ PK: { S: mutual }, SK: { S: '#METADATA#' } });
213
- });
214
-
215
- const deleteResults = await Promise.allSettled(
216
- itemsToDelete.map((item) =>
217
- dynamodbClient.deleteItem({
218
- TableName,
219
- Key: {
220
- PK: item.PK,
221
- SK: item.SK,
222
- },
223
- }),
224
- ),
225
- );
226
- errorContext.deleteResults = deleteResults;
227
-
228
- if (
229
- deleteResults.some(
230
- (result) =>
231
- result.status === 'rejected' &&
232
- !(result.reason instanceof ConditionalCheckFailedException),
233
- )
234
- ) {
235
- throw new StandardError(
236
- 'REPLICATION_ERROR',
237
- 'Replication error',
238
- null,
239
- errorContext,
240
- );
241
- }
242
- }
243
- } catch (error) {
244
- console.error('====REPLICATION_ERROR', error);
245
- console.log(
246
- '====REPLICATION_ERROR errorContext',
247
- JSON.stringify(errorContext, null, 2),
248
- );
249
-
250
- batchItemFailures.push({
251
- itemIdentifier: record.dynamodb?.SequenceNumber || '',
252
- });
253
-
254
- // immediately return to prevent processing the rest
255
- // because stream will restart from this point again
256
- return { batchItemFailures };
257
- }
258
- }
259
-
260
- return { batchItemFailures };
261
- };
@@ -1,174 +0,0 @@
1
- import type { CreatedEntity, Entity as EntityType } from '@monorise/base';
2
- import type { SQSBatchItemFailure, SQSEvent } from 'aws-lambda';
3
- import { EntityConfig } from '#/lambda-layer/monorise';
4
- import type { Entity } from '../data/Entity';
5
- import { parseSQSBusEvent } from '../helpers/event';
6
- import { DependencyContainer } from '../services/DependencyContainer';
7
- import type { Tag } from '../types/entity.type';
8
-
9
- export type EventDetailBody = {
10
- entityType: EntityType;
11
- entityId: string;
12
- data: Record<string, any>;
13
- };
14
-
15
- const container = new DependencyContainer();
16
-
17
- function compareTags(
18
- existingTags: Tag[],
19
- newTags: Tag[],
20
- ): {
21
- old: Tag[];
22
- new: Tag[];
23
- remain: Tag[];
24
- } {
25
- const oldMap = new Map(
26
- existingTags.map((item) => [`${item.group}#${item.sortValue}`, item]),
27
- );
28
- const newMap = new Map(
29
- newTags.map((item) => [`${item.group}#${item.sortValue}`, item]),
30
- );
31
-
32
- const remain: Tag[] = [];
33
- const oldDiff: Tag[] = [];
34
- const newDiff: Tag[] = [];
35
-
36
- for (const [key, oldItem] of oldMap.entries()) {
37
- if (newMap.has(key)) {
38
- remain.push(oldItem);
39
- newMap.delete(key); // Remove from newMap as it's already in remain
40
- } else {
41
- oldDiff.push(oldItem);
42
- }
43
- }
44
-
45
- // Remaining entries in newMap are new
46
- for (const newItem of newMap.values()) {
47
- newDiff.push(newItem);
48
- }
49
-
50
- return {
51
- old: oldDiff,
52
- new: newDiff,
53
- remain,
54
- };
55
- }
56
-
57
- async function batchUpdateTags({
58
- tagName,
59
- entity,
60
- diff,
61
- }: {
62
- tagName: string;
63
- entity: Entity<EntityType>;
64
- diff: { old: Tag[]; new: Tag[] };
65
- }): Promise<void> {
66
- if (!entity.entityId) {
67
- throw new Error('entityId is required');
68
- }
69
-
70
- const { old: tagsToRemove, new: tagsToAdd } = diff;
71
- const { entityType, entityId } = entity;
72
-
73
- const removePromises = tagsToRemove.reduce(
74
- (acc, tag) => [
75
- ...acc,
76
- container.tagRepository.deleteTag({
77
- tagName,
78
- group: tag.group,
79
- sortValue: tag.sortValue,
80
- entityType,
81
- entityId,
82
- }),
83
- ],
84
- [] as Promise<any>[],
85
- );
86
-
87
- const addPromises = tagsToAdd.reduce(
88
- (acc, tag) => [
89
- ...acc,
90
- container.tagRepository.createTag({
91
- tagName,
92
- group: tag.group,
93
- sortValue: tag.sortValue,
94
- entity,
95
- }),
96
- ],
97
- [] as Promise<any>[],
98
- );
99
-
100
- await Promise.all([...removePromises, ...addPromises]);
101
- }
102
-
103
- export const handler = async (ev: SQSEvent) => {
104
- const batchItemFailures: SQSBatchItemFailure[] = [];
105
-
106
- for (const record of ev.Records) {
107
- const body = parseSQSBusEvent<EventDetailBody>(record.body);
108
- const { detail } = body;
109
- const { entityType, entityId } = detail;
110
-
111
- const errorContext: Record<string, unknown> = {};
112
- errorContext.body = body;
113
-
114
- try {
115
- const tagConfigs = EntityConfig[entityType]?.tags;
116
-
117
- if (!tagConfigs || !tagConfigs.length) {
118
- // skip if entity has no tag configs
119
- continue;
120
- }
121
-
122
- await container.tagRepository.createLock({
123
- entityType,
124
- entityId,
125
- });
126
-
127
- for (const tagConfig of tagConfigs) {
128
- const { name, processor } = tagConfig;
129
-
130
- const existingTags = await container.tagRepository.getExistingTags({
131
- entityType,
132
- entityId,
133
- tagName: name,
134
- });
135
- errorContext.existingTags = existingTags;
136
-
137
- const entity = await container.entityRepository.getEntity(
138
- entityType,
139
- entityId,
140
- );
141
- errorContext.entity = entity;
142
-
143
- const newTags = await processor(
144
- entity as unknown as CreatedEntity<EntityType>,
145
- );
146
- errorContext.newTags = newTags;
147
-
148
- const diff = compareTags(existingTags, newTags);
149
- errorContext.diff = diff;
150
-
151
- await batchUpdateTags({
152
- tagName: name,
153
- entity,
154
- diff,
155
- });
156
- }
157
-
158
- await container.tagRepository.deleteLock({
159
- entityType,
160
- entityId,
161
- });
162
- } catch (err) {
163
- console.log(
164
- '===TAG-PROCESSOR ERROR===',
165
- err,
166
- JSON.stringify({ errorContext }, null, 2),
167
- );
168
-
169
- batchItemFailures.push({ itemIdentifier: record.messageId });
170
- }
171
- }
172
-
173
- return { batchItemFailures };
174
- };