@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,62 +0,0 @@
1
- import { createEntityConfig } from '@monorise/cli';
2
- import { z } from 'zod';
3
-
4
- const baseSchema = z.object({
5
- activityType: z.enum(['REFLECTION', 'QUIZ', 'FILE_UPLOAD']),
6
- questionType: z.enum(['TEXT', 'RADIO', 'CHECKBOX']).optional(),
7
- question: z.string(),
8
- explanation: z.string().optional(),
9
- options: z
10
- .object({
11
- label: z.string(),
12
- isCorrect: z.boolean(),
13
- })
14
- .array()
15
- .optional(),
16
- remark: z.string().optional(),
17
- });
18
-
19
- const config = createEntityConfig({
20
- name: 'learning-activity',
21
- displayName: 'Learning Activity',
22
- baseSchema,
23
- effect: (schema) => {
24
- return schema.refine(
25
- (value) => {
26
- if (value.activityType === 'REFLECTION' && value.question) {
27
- return true;
28
- }
29
-
30
- if (value.activityType === 'FILE_UPLOAD' && value.question) {
31
- return true;
32
- }
33
-
34
- if (
35
- (value.questionType === 'CHECKBOX' ||
36
- value.questionType === 'RADIO') &&
37
- value.options?.length &&
38
- value.options.length >= 2 &&
39
- value.options.filter((v) => v.label).length ===
40
- value.options.length &&
41
- value.options.some((v) => v.isCorrect)
42
- ) {
43
- return true;
44
- }
45
-
46
- if (value.questionType === 'TEXT' && value.question) {
47
- return true;
48
- }
49
-
50
- return false;
51
- },
52
- {
53
- message:
54
- 'For checkbox and radio question types, please provide at least two options and at least one correct answer',
55
- path: ['options'],
56
- },
57
- );
58
- },
59
- searchableFields: ['question', 'remark'],
60
- });
61
-
62
- export default config;
@@ -1,34 +0,0 @@
1
- import { createEntityConfig } from '@monorise/cli';
2
- import type { Mutual } from '@monorise/react';
3
- import { z } from 'zod';
4
- import { Entity } from '../entity';
5
-
6
- const baseSchema = z.object({}).partial();
7
-
8
- const mutualSchema = z
9
- .object({
10
- courseOrders: z.string().array(),
11
- })
12
- .partial();
13
-
14
- const config = createEntityConfig({
15
- name: 'learning-journey-config',
16
- displayName: 'Learning Journey Config',
17
- baseSchema,
18
- mutual: {
19
- mutualSchema,
20
- mutualFields: {
21
- courseOrders: {
22
- entityType: Entity.COURSE,
23
- mutualDataProcessor: (
24
- ids: string[],
25
- context: Mutual<Entity.LEARNING_JOURNEY_CONFIG, Entity.COURSE>,
26
- ) => {
27
- return { index: ids.indexOf(context.entityId) };
28
- },
29
- },
30
- },
31
- },
32
- });
33
-
34
- export default config;
@@ -1,108 +0,0 @@
1
- import { createEntityConfig } from '@monorise/cli';
2
- import type { Mutual } from '@monorise/react';
3
- import { z } from 'zod';
4
- import { Entity } from '../entity';
5
-
6
- const allowedTypes = ['MODULE', 'TOUCHPOINT', 'CHECKPOINT'] as const;
7
-
8
- const baseSchema = z
9
- .object({
10
- title: z.string(),
11
- description: z.string(),
12
- shortDescription: z.string(),
13
- remark: z
14
- .string()
15
- .describe('For internal use, eg. This module is for tracks'),
16
- type: z.enum(allowedTypes),
17
- })
18
- .partial();
19
-
20
- const createSchema = baseSchema.extend({
21
- title: z.string(),
22
- type: z.enum(allowedTypes),
23
- });
24
-
25
- const mutualSchema = z
26
- .object({
27
- chapterOrders: z.string().array(),
28
- })
29
- .partial();
30
-
31
- const config = createEntityConfig({
32
- name: 'module',
33
- displayName: 'Module',
34
- baseSchema,
35
- createSchema,
36
- searchableFields: ['title'],
37
- mutual: {
38
- subscribes: [{ entityType: Entity.CHAPTER }],
39
- mutualSchema,
40
- mutualFields: {
41
- chapterOrders: {
42
- entityType: Entity.CHAPTER,
43
- mutualDataProcessor: (
44
- ids: string[],
45
- context: Mutual<Entity.MODULE, Entity.CHAPTER>,
46
- ) => {
47
- return { index: ids.indexOf(context.entityId) };
48
- },
49
- },
50
- videos: {
51
- entityType: Entity.VIDEO,
52
- mutualDataProcessor: (
53
- ids: string[],
54
- context: Mutual<Entity.MODULE, Entity.VIDEO>,
55
- prejoinContext?: Record<string, any>,
56
- ) => ({
57
- index: ids.indexOf(context.entityId),
58
- chapterId: prejoinContext?.[context.entityId],
59
- }),
60
- },
61
- learningActivities: {
62
- entityType: Entity.LEARNING_ACTIVITY,
63
- mutualDataProcessor: (
64
- ids: string[],
65
- context: Mutual<Entity.MODULE, Entity.LEARNING_ACTIVITY>,
66
- prejoinContext?: Record<string, any>,
67
- ) => ({
68
- index: ids.indexOf(context.entityId),
69
- chapterId: prejoinContext?.[context.entityId],
70
- }),
71
- },
72
- },
73
- prejoins: [
74
- {
75
- mutualField: 'videos',
76
- targetEntityType: Entity.VIDEO,
77
- entityPaths: [
78
- {
79
- entityType: Entity.MODULE,
80
- },
81
- {
82
- entityType: Entity.CHAPTER,
83
- },
84
- {
85
- entityType: Entity.VIDEO,
86
- },
87
- ],
88
- },
89
- {
90
- mutualField: 'learningActivities',
91
- targetEntityType: Entity.LEARNING_ACTIVITY,
92
- entityPaths: [
93
- {
94
- entityType: Entity.MODULE,
95
- },
96
- {
97
- entityType: Entity.CHAPTER,
98
- },
99
- {
100
- entityType: Entity.LEARNING_ACTIVITY,
101
- },
102
- ],
103
- },
104
- ],
105
- },
106
- });
107
-
108
- export default config;
@@ -1,63 +0,0 @@
1
- import { createEntityConfig } from '@monorise/cli';
2
- import { z } from 'zod';
3
-
4
- import type { Mutual } from '@monorise/react';
5
- import { Entity } from '../entity';
6
-
7
- export enum EXPIRY_TYPE {
8
- ORG_WIDE = 'ORG_WIDE',
9
- PER_INDIVIDUAL = 'PER_INDIVIDUAL',
10
- }
11
-
12
- const baseSchema = z
13
- .object({
14
- name: z.string(),
15
- licenseCount: z.coerce.number(),
16
- allowSelfRegistration: z.boolean(),
17
- isPrimary: z.boolean(),
18
- domains: z.string().array(),
19
- selfRegistrationDisclaimer: z.string(),
20
- welcomeMessage: z.string(),
21
- pathwayWelcomeMessage: z.string(),
22
- expiryType: z.nativeEnum(EXPIRY_TYPE),
23
- orgExpiryDate: z.string().datetime(),
24
- individualAccessDuration: z.coerce.number(),
25
- })
26
- .partial();
27
-
28
- const createSchema = baseSchema.extend({
29
- name: z.string().min(1, 'Please provide a name for this organization'),
30
- });
31
-
32
- const mutualSchema = z
33
- .object({
34
- organizationOrders: z.string().array(),
35
- })
36
- .partial();
37
-
38
- const config = createEntityConfig({
39
- name: 'organization',
40
- displayName: 'Organization',
41
- baseSchema,
42
- createSchema,
43
- searchableFields: ['name'],
44
- mutual: {
45
- mutualSchema,
46
- mutualFields: {
47
- organizationOrders: {
48
- entityType: Entity.ORGANIZATION,
49
- mutualDataProcessor: (
50
- ids: string[],
51
- context: Mutual<Entity.ORGANIZATION, Entity.ORGANIZATION>,
52
- ) => {
53
- return {
54
- index: ids.indexOf(context.entityId),
55
- primaryOrganizationId: context.byEntityId,
56
- };
57
- },
58
- },
59
- },
60
- },
61
- });
62
-
63
- export default config;
@@ -1,28 +0,0 @@
1
- import { createEntityConfig } from '@monorise/cli';
2
- import { z } from 'zod';
3
-
4
- const baseSchema = z
5
- .object({
6
- label: z.string(),
7
- link: z.string(),
8
- prelabel: z.string(),
9
- postlabel: z.string(),
10
- })
11
- .partial();
12
-
13
- const createSchema = baseSchema.extend({
14
- label: z.string().min(1, {
15
- message: 'Label is required',
16
- }),
17
- link: z.string().url('This is not a valid url'),
18
- });
19
-
20
- const config = createEntityConfig({
21
- name: 'reference',
22
- displayName: 'Reference',
23
- baseSchema,
24
- createSchema,
25
- searchableFields: ['label', 'link', 'prelabel', 'postlabel'],
26
- });
27
-
28
- export default config;
@@ -1,36 +0,0 @@
1
- import { createEntityConfig } from '@monorise/cli';
2
- import { z } from 'zod';
3
-
4
- const baseSchema = z
5
- .object({
6
- title: z.string(),
7
- categories: z.string().array(),
8
- additionalFiles: z
9
- .object({
10
- type: z.string(),
11
- id: z.string(),
12
- content: z.any(),
13
- })
14
- .array()
15
- .optional(),
16
- keyTakeaway: z.string(),
17
- remark: z.string(),
18
- duration: z.number(),
19
- })
20
- .partial();
21
-
22
- const createSchema = baseSchema.extend({
23
- title: z.string().min(4, {
24
- message: 'Title must be at least 4 characters.',
25
- }),
26
- });
27
-
28
- const config = createEntityConfig({
29
- name: 'video',
30
- displayName: 'Video',
31
- baseSchema,
32
- createSchema,
33
- searchableFields: ['title', 'remark'],
34
- });
35
-
36
- export default config;
@@ -1,55 +0,0 @@
1
- import type { Entity } from '@monorise/base';
2
- import type { SQSBatchItemFailure, SQSEvent } from 'aws-lambda';
3
- import { StandardError } from '../errors/standard-error';
4
- import { parseSQSBusEvent } from '../helpers/event';
5
- import { DependencyContainer } from '../services/DependencyContainer';
6
-
7
- const container = new DependencyContainer();
8
-
9
- type EventDetailBody = {
10
- entityType: Entity;
11
- entityId?: string;
12
- entityPayload: Record<string, any>;
13
- accountId?: string;
14
- options: {
15
- createAndUpdateDatetime?: string;
16
- mutualId?: string;
17
- };
18
- };
19
-
20
- export const handler = async (ev: SQSEvent) => {
21
- const { entityService } = container;
22
- const batchItemFailures: SQSBatchItemFailure[] = [];
23
-
24
- for (const record of ev.Records) {
25
- const errorContext: Record<string, unknown> = {};
26
- const body = parseSQSBusEvent<EventDetailBody>(record.body);
27
- const { detail } = body;
28
- const { entityType, entityId, entityPayload, accountId, options } = detail;
29
- errorContext.body = body;
30
-
31
- try {
32
- await entityService.createEntity({
33
- entityType,
34
- entityId,
35
- entityPayload,
36
- accountId,
37
- options,
38
- });
39
- } catch (err) {
40
- console.error(
41
- '=====CREATE_ENTITY_PROCESSOR_ERROR=====',
42
- err,
43
- JSON.stringify({ errorContext }, null, 2),
44
- );
45
-
46
- if (err instanceof StandardError && err.code === 'INVALID_ENTITY_TYPE') {
47
- continue; // do not retry
48
- }
49
-
50
- batchItemFailures.push({ itemIdentifier: record.messageId });
51
- }
52
- }
53
-
54
- return { batchItemFailures };
55
- };
@@ -1,262 +0,0 @@
1
- import { TransactionCanceledException } from '@aws-sdk/client-dynamodb';
2
- import type { Entity } from '@monorise/base';
3
- import type { SQSBatchItemFailure, SQSEvent } from 'aws-lambda';
4
- import { EntityConfig } from '#/lambda-layer/monorise';
5
- import { Mutual } from '../data/Mutual';
6
- import { StandardError } from '../errors/standard-error';
7
- import { parseSQSBusEvent } from '../helpers/event';
8
- import { DependencyContainer } from '../services/DependencyContainer';
9
- import { EVENT } from '../types/event';
10
-
11
- export type EventDetailBody = {
12
- mutualIds: string[];
13
- byEntityType: Entity;
14
- byEntityId: string;
15
- entityType: Entity;
16
- field: string;
17
- publishedAt: string;
18
- customContext?: Record<string, unknown>;
19
- };
20
-
21
- const container = new DependencyContainer();
22
-
23
- const processEntities = async (
24
- entityIds: string[],
25
- action: (id: string) => Promise<void>,
26
- ) => Promise.allSettled(entityIds.map(action));
27
-
28
- export const handler = async (ev: SQSEvent) => {
29
- const batchItemFailures: SQSBatchItemFailure[] = [];
30
- const { entityRepository, mutualRepository, publishEvent } = container;
31
-
32
- await Promise.allSettled(
33
- ev.Records.map(async (record) => {
34
- const errorContext: Record<string, unknown> = {};
35
- const body = parseSQSBusEvent<EventDetailBody>(record.body);
36
- const { detail } = body;
37
- const {
38
- mutualIds,
39
- byEntityType,
40
- byEntityId,
41
- entityType,
42
- field,
43
- publishedAt,
44
- customContext,
45
- } = detail;
46
- errorContext.body = body;
47
-
48
- try {
49
- // Validate if mutual configuration exists
50
- const config =
51
- EntityConfig[byEntityType]?.mutual?.mutualFields?.[field];
52
-
53
- if (!config) {
54
- throw new StandardError('INVALID_MUTUAL', 'Invalid mutual');
55
- }
56
-
57
- const mutualDataProcessor = config.mutualDataProcessor ?? (() => ({}));
58
-
59
- // Create a lock to prevent concurrent modifications
60
- await mutualRepository.createMutualLock({
61
- byEntityType,
62
- byEntityId,
63
- entityType,
64
- version: publishedAt,
65
- });
66
-
67
- // Fetch related entities in parallel
68
- const [byEntity, mutuals] = await Promise.all([
69
- entityRepository.getEntity(byEntityType, byEntityId),
70
- mutualRepository.listEntitiesByEntity(
71
- byEntityType,
72
- byEntityId,
73
- entityType,
74
- ),
75
- ]);
76
-
77
- // Determine which entities were added, removed, or need updates
78
- const existingEntityIds = new Set(mutuals.items.map((m) => m.entityId));
79
- const newMutualIds = new Set(mutualIds ?? []);
80
-
81
- const addedEntityIds = [...newMutualIds].filter(
82
- (id) => !existingEntityIds.has(id),
83
- );
84
- const deletedEntityIds = [...existingEntityIds].filter(
85
- (id) => !newMutualIds.has(id),
86
- );
87
- const toUpdateEntityIds = [...existingEntityIds].filter((id) =>
88
- newMutualIds.has(id),
89
- );
90
-
91
- errorContext.existingEntityIds = [...existingEntityIds];
92
- errorContext.addedEntityIds = addedEntityIds;
93
- errorContext.deletedEntityIds = deletedEntityIds;
94
- errorContext.toUpdateEntityIds = toUpdateEntityIds;
95
-
96
- const addEntities = await processEntities(
97
- addedEntityIds,
98
- async (id) => {
99
- const entity = await entityRepository.getEntity(entityType, id);
100
- await mutualRepository.createMutual(
101
- byEntityType,
102
- byEntityId,
103
- byEntity.data,
104
- entityType,
105
- id,
106
- entity.data,
107
- mutualDataProcessor(
108
- mutualIds,
109
- new Mutual(
110
- byEntityType,
111
- byEntityId,
112
- byEntity.data,
113
- entityType,
114
- id,
115
- entity.data,
116
- {},
117
- ),
118
- customContext,
119
- ),
120
- {
121
- ConditionExpression:
122
- 'attribute_not_exists(#mutualUpdatedAt) OR #mutualUpdatedAt < :publishedAt',
123
- ExpressionAttributeNames: {
124
- '#mutualUpdatedAt': 'mutualUpdatedAt',
125
- },
126
- ExpressionAttributeValues: {
127
- ':publishedAt': { S: publishedAt },
128
- },
129
- createAndUpdateDatetime: new Date(publishedAt),
130
- },
131
- );
132
- },
133
- );
134
-
135
- const deleteEntities = await processEntities(
136
- deletedEntityIds,
137
- async (id) => {
138
- await mutualRepository.deleteMutual(
139
- byEntityType,
140
- byEntityId,
141
- entityType,
142
- id,
143
- {
144
- ConditionExpression:
145
- 'attribute_exists(PK) AND #mutualUpdatedAt < :publishedAt',
146
- ExpressionAttributeNames: {
147
- '#mutualUpdatedAt': 'mutualUpdatedAt',
148
- },
149
- ExpressionAttributeValues: {
150
- ':publishedAt': { S: publishedAt },
151
- },
152
- },
153
- );
154
- },
155
- );
156
-
157
- const updateEntities = await processEntities(
158
- toUpdateEntityIds,
159
- async (id) => {
160
- await mutualRepository.updateMutual(
161
- byEntityType,
162
- byEntityId,
163
- entityType,
164
- id,
165
- {
166
- mutualData: mutualDataProcessor(
167
- mutualIds,
168
- new Mutual(
169
- byEntityType,
170
- byEntityId,
171
- byEntity.data,
172
- entityType,
173
- id,
174
- {},
175
- {},
176
- ),
177
- customContext,
178
- ),
179
- mutualUpdatedAt: publishedAt,
180
- },
181
- {
182
- ConditionExpression:
183
- 'attribute_exists(PK) AND #mutualUpdatedAt < :publishedAt',
184
- ExpressionAttributeNames: {
185
- '#mutualUpdatedAt': 'mutualUpdatedAt',
186
- },
187
- ExpressionAttributeValues: {
188
- ':publishedAt': { S: publishedAt },
189
- },
190
- },
191
- );
192
- },
193
- );
194
-
195
- errorContext.results = { addEntities, deleteEntities, updateEntities };
196
-
197
- // release lock
198
- await mutualRepository.deleteMutualLock({
199
- byEntityType,
200
- byEntityId,
201
- entityType,
202
- });
203
-
204
- // Check for unprocessable errors in processing results
205
- if (
206
- [...addEntities, ...deleteEntities, ...updateEntities].some(
207
- (res) =>
208
- res.status === 'rejected' &&
209
- !(
210
- res.reason instanceof TransactionCanceledException ||
211
- (res.reason instanceof StandardError &&
212
- res.reason.code === 'MUTUAL_NOT_FOUND')
213
- ),
214
- )
215
- ) {
216
- throw new StandardError(
217
- 'MUTUAL_PROCESSOR_ERROR',
218
- 'Mutual processor error',
219
- null,
220
- errorContext,
221
- );
222
- }
223
-
224
- await publishEvent({
225
- event: EVENT.CORE.ENTITY_MUTUAL_PROCESSED,
226
- payload: {
227
- byEntityType,
228
- byEntityId,
229
- entityType,
230
- field,
231
- mutualIds,
232
- publishedAt,
233
- },
234
- });
235
- } catch (err) {
236
- console.error(
237
- '=====MUTUAL_PROCESSOR_ERROR=====',
238
- err,
239
- JSON.stringify({ errorContext }, null, 2),
240
- );
241
-
242
- // Release the lock to avoid deadlocks
243
- await mutualRepository.deleteMutualLock({
244
- byEntityType,
245
- byEntityId,
246
- entityType,
247
- });
248
-
249
- if (
250
- err instanceof StandardError &&
251
- ['INVALID_MUTUAL', 'MUTUAL_LOCK_CONFLICT'].includes(err.code)
252
- ) {
253
- return;
254
- }
255
-
256
- batchItemFailures.push({ itemIdentifier: record.messageId });
257
- }
258
- }),
259
- );
260
-
261
- return { batchItemFailures };
262
- };