@monorise/core 1.0.4-6 → 3.0.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 (198) hide show
  1. package/dist/constants/table.d.ts +0 -1
  2. package/dist/constants/table.d.ts.map +1 -1
  3. package/dist/constants/table.js +0 -1
  4. package/dist/constants/table.js.map +1 -1
  5. package/dist/controllers/entity/create-entity.controller.d.ts +1 -2
  6. package/dist/controllers/entity/create-entity.controller.d.ts.map +1 -1
  7. package/dist/controllers/entity/create-entity.controller.js +54 -34
  8. package/dist/controllers/entity/create-entity.controller.js.map +1 -1
  9. package/dist/controllers/entity/delete-entity.controller.d.ts +1 -2
  10. package/dist/controllers/entity/delete-entity.controller.d.ts.map +1 -1
  11. package/dist/controllers/entity/delete-entity.controller.js +30 -21
  12. package/dist/controllers/entity/delete-entity.controller.js.map +1 -1
  13. package/dist/controllers/entity/get-entity-by-unique-field-value.controller.d.ts +7 -0
  14. package/dist/controllers/entity/get-entity-by-unique-field-value.controller.d.ts.map +1 -0
  15. package/dist/controllers/entity/get-entity-by-unique-field-value.controller.js +36 -0
  16. package/dist/controllers/entity/get-entity-by-unique-field-value.controller.js.map +1 -0
  17. package/dist/controllers/entity/get-entity.controller.d.ts +1 -2
  18. package/dist/controllers/entity/get-entity.controller.d.ts.map +1 -1
  19. package/dist/controllers/entity/get-entity.controller.js +30 -17
  20. package/dist/controllers/entity/get-entity.controller.js.map +1 -1
  21. package/dist/controllers/entity/list-entities.controller.d.ts +1 -2
  22. package/dist/controllers/entity/list-entities.controller.d.ts.map +1 -1
  23. package/dist/controllers/entity/list-entities.controller.js +44 -39
  24. package/dist/controllers/entity/list-entities.controller.js.map +1 -1
  25. package/dist/controllers/entity/update-entity.controller.d.ts +1 -2
  26. package/dist/controllers/entity/update-entity.controller.d.ts.map +1 -1
  27. package/dist/controllers/entity/update-entity.controller.js +52 -35
  28. package/dist/controllers/entity/update-entity.controller.js.map +1 -1
  29. package/dist/controllers/entity/upsert-entity.controller.d.ts +3 -3
  30. package/dist/controllers/entity/upsert-entity.controller.d.ts.map +1 -1
  31. package/dist/controllers/entity/upsert-entity.controller.js +70 -60
  32. package/dist/controllers/entity/upsert-entity.controller.js.map +1 -1
  33. package/dist/controllers/mutual/create-mutual.controller.d.ts +2 -4
  34. package/dist/controllers/mutual/create-mutual.controller.d.ts.map +1 -1
  35. package/dist/controllers/mutual/create-mutual.controller.js +51 -45
  36. package/dist/controllers/mutual/create-mutual.controller.js.map +1 -1
  37. package/dist/controllers/mutual/delete-mutual.controller.d.ts +1 -2
  38. package/dist/controllers/mutual/delete-mutual.controller.d.ts.map +1 -1
  39. package/dist/controllers/mutual/delete-mutual.controller.js +32 -23
  40. package/dist/controllers/mutual/delete-mutual.controller.js.map +1 -1
  41. package/dist/controllers/mutual/get-mutual.controller.d.ts +1 -2
  42. package/dist/controllers/mutual/get-mutual.controller.d.ts.map +1 -1
  43. package/dist/controllers/mutual/get-mutual.controller.js +29 -17
  44. package/dist/controllers/mutual/get-mutual.controller.js.map +1 -1
  45. package/dist/controllers/mutual/list-entities-by-entity.controller.d.ts +1 -2
  46. package/dist/controllers/mutual/list-entities-by-entity.controller.d.ts.map +1 -1
  47. package/dist/controllers/mutual/list-entities-by-entity.controller.js +60 -44
  48. package/dist/controllers/mutual/list-entities-by-entity.controller.js.map +1 -1
  49. package/dist/controllers/mutual/update-mutual.controller.d.ts +1 -2
  50. package/dist/controllers/mutual/update-mutual.controller.d.ts.map +1 -1
  51. package/dist/controllers/mutual/update-mutual.controller.js +44 -30
  52. package/dist/controllers/mutual/update-mutual.controller.js.map +1 -1
  53. package/dist/controllers/setupRoutes.d.ts +2 -3
  54. package/dist/controllers/setupRoutes.d.ts.map +1 -1
  55. package/dist/controllers/setupRoutes.js +19 -17
  56. package/dist/controllers/setupRoutes.js.map +1 -1
  57. package/dist/controllers/tag/list-tags.controller.d.ts +1 -2
  58. package/dist/controllers/tag/list-tags.controller.d.ts.map +1 -1
  59. package/dist/controllers/tag/list-tags.controller.js +42 -33
  60. package/dist/controllers/tag/list-tags.controller.js.map +1 -1
  61. package/dist/data/DbUtils.js +24 -13
  62. package/dist/data/DbUtils.js.map +1 -1
  63. package/dist/data/Entity.d.ts +14 -10
  64. package/dist/data/Entity.d.ts.map +1 -1
  65. package/dist/data/Entity.js +370 -227
  66. package/dist/data/Entity.js.map +1 -1
  67. package/dist/data/EventUtils.d.ts +3 -3
  68. package/dist/data/EventUtils.d.ts.map +1 -1
  69. package/dist/data/EventUtils.js +35 -27
  70. package/dist/data/EventUtils.js.map +1 -1
  71. package/dist/data/Mutual.d.ts +7 -8
  72. package/dist/data/Mutual.d.ts.map +1 -1
  73. package/dist/data/Mutual.js +288 -341
  74. package/dist/data/Mutual.js.map +1 -1
  75. package/dist/data/Tag.d.ts +2 -2
  76. package/dist/data/Tag.d.ts.map +1 -1
  77. package/dist/data/Tag.js +229 -230
  78. package/dist/data/Tag.js.map +1 -1
  79. package/dist/data/abstract/Repository.base.js +4 -7
  80. package/dist/data/abstract/Repository.base.js.map +1 -1
  81. package/dist/errors/extendable-error.js +0 -7
  82. package/dist/errors/extendable-error.js.map +1 -1
  83. package/dist/errors/standard-error.d.ts +22 -0
  84. package/dist/errors/standard-error.d.ts.map +1 -1
  85. package/dist/errors/standard-error.js +21 -3
  86. package/dist/errors/standard-error.js.map +1 -1
  87. package/dist/handles/app.d.ts +16 -0
  88. package/dist/handles/app.d.ts.map +1 -0
  89. package/dist/handles/app.js +31 -0
  90. package/dist/handles/app.js.map +1 -0
  91. package/dist/helpers/event.d.ts.map +1 -1
  92. package/dist/helpers/event.js +35 -34
  93. package/dist/helpers/event.js.map +1 -1
  94. package/dist/index.d.ts +23 -16
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +12 -21
  97. package/dist/index.js.map +1 -1
  98. package/dist/middlewares/api-key-auth.d.ts +3 -0
  99. package/dist/middlewares/api-key-auth.d.ts.map +1 -0
  100. package/dist/middlewares/api-key-auth.js +32 -0
  101. package/dist/middlewares/api-key-auth.js.map +1 -0
  102. package/dist/middlewares/entity-type-check.d.ts +1 -1
  103. package/dist/middlewares/entity-type-check.d.ts.map +1 -1
  104. package/dist/middlewares/entity-type-check.js +17 -7
  105. package/dist/middlewares/entity-type-check.js.map +1 -1
  106. package/dist/middlewares/general-error-handler.d.ts +7 -0
  107. package/dist/middlewares/general-error-handler.d.ts.map +1 -0
  108. package/dist/middlewares/general-error-handler.js +40 -0
  109. package/dist/middlewares/general-error-handler.js.map +1 -0
  110. package/dist/middlewares/mutual-type-check.d.ts +1 -1
  111. package/dist/middlewares/mutual-type-check.d.ts.map +1 -1
  112. package/dist/middlewares/mutual-type-check.js +18 -8
  113. package/dist/middlewares/mutual-type-check.js.map +1 -1
  114. package/dist/processors/create-entity-processor.d.ts.map +1 -1
  115. package/dist/processors/create-entity-processor.js +17 -5
  116. package/dist/processors/create-entity-processor.js.map +1 -1
  117. package/dist/processors/mutual-processor.d.ts.map +1 -1
  118. package/dist/processors/mutual-processor.js +39 -29
  119. package/dist/processors/mutual-processor.js.map +1 -1
  120. package/dist/processors/prejoin-processor.d.ts.map +1 -1
  121. package/dist/processors/prejoin-processor.js +117 -104
  122. package/dist/processors/prejoin-processor.js.map +1 -1
  123. package/dist/processors/replication-processor.d.ts.map +1 -1
  124. package/dist/processors/replication-processor.js +31 -31
  125. package/dist/processors/replication-processor.js.map +1 -1
  126. package/dist/processors/tag-processor.js +48 -35
  127. package/dist/processors/tag-processor.js.map +1 -1
  128. package/dist/services/DependencyContainer.d.ts +16 -7
  129. package/dist/services/DependencyContainer.d.ts.map +1 -1
  130. package/dist/services/DependencyContainer.js +13 -24
  131. package/dist/services/DependencyContainer.js.map +1 -1
  132. package/dist/services/entity-service-lifecycle.js +29 -20
  133. package/dist/services/entity-service-lifecycle.js.map +1 -1
  134. package/dist/services/entity.service.d.ts +6 -6
  135. package/dist/services/entity.service.d.ts.map +1 -1
  136. package/dist/services/entity.service.js +96 -93
  137. package/dist/services/entity.service.js.map +1 -1
  138. package/dist/services/mutual.service.js +117 -113
  139. package/dist/services/mutual.service.js.map +1 -1
  140. package/dist/tsconfig.tsbuildinfo +1 -0
  141. package/dist/types/entity.type.d.ts.map +1 -1
  142. package/dist/types/event.d.ts +4 -3
  143. package/dist/types/event.d.ts.map +1 -1
  144. package/dist/types/event.js +9 -9
  145. package/dist/types/event.js.map +1 -1
  146. package/dist/vitest.config.d.ts +3 -0
  147. package/dist/vitest.config.d.ts.map +1 -0
  148. package/dist/vitest.config.js +10 -0
  149. package/dist/vitest.config.js.map +1 -0
  150. package/package.json +8 -6
  151. package/dist/mock/entity.d.ts +0 -13
  152. package/dist/mock/entity.d.ts.map +0 -1
  153. package/dist/mock/entity.js +0 -14
  154. package/dist/mock/entity.js.map +0 -1
  155. package/dist/mock/monorise/admin.d.ts +0 -127
  156. package/dist/mock/monorise/admin.d.ts.map +0 -1
  157. package/dist/mock/monorise/admin.js +0 -29
  158. package/dist/mock/monorise/admin.js.map +0 -1
  159. package/dist/mock/monorise/chapter.d.ts +0 -457
  160. package/dist/mock/monorise/chapter.d.ts.map +0 -1
  161. package/dist/mock/monorise/chapter.js +0 -81
  162. package/dist/mock/monorise/chapter.js.map +0 -1
  163. package/dist/mock/monorise/course.d.ts +0 -311
  164. package/dist/mock/monorise/course.d.ts.map +0 -1
  165. package/dist/mock/monorise/course.js +0 -128
  166. package/dist/mock/monorise/course.js.map +0 -1
  167. package/dist/mock/monorise/index.d.ts +0 -3599
  168. package/dist/mock/monorise/index.d.ts.map +0 -1
  169. package/dist/mock/monorise/index.js +0 -62
  170. package/dist/mock/monorise/index.js.map +0 -1
  171. package/dist/mock/monorise/learner.d.ts +0 -390
  172. package/dist/mock/monorise/learner.d.ts.map +0 -1
  173. package/dist/mock/monorise/learner.js +0 -59
  174. package/dist/mock/monorise/learner.js.map +0 -1
  175. package/dist/mock/monorise/learning-activity.d.ts +0 -266
  176. package/dist/mock/monorise/learning-activity.d.ts.map +0 -1
  177. package/dist/mock/monorise/learning-activity.js +0 -50
  178. package/dist/mock/monorise/learning-activity.js.map +0 -1
  179. package/dist/mock/monorise/learning-journey-config.d.ts +0 -84
  180. package/dist/mock/monorise/learning-journey-config.d.ts.map +0 -1
  181. package/dist/mock/monorise/learning-journey-config.js +0 -27
  182. package/dist/mock/monorise/learning-journey-config.js.map +0 -1
  183. package/dist/mock/monorise/module.d.ts +0 -211
  184. package/dist/mock/monorise/module.d.ts.map +0 -1
  185. package/dist/mock/monorise/module.js +0 -91
  186. package/dist/mock/monorise/module.js.map +0 -1
  187. package/dist/mock/monorise/organization.d.ts +0 -346
  188. package/dist/mock/monorise/organization.d.ts.map +0 -1
  189. package/dist/mock/monorise/organization.js +0 -54
  190. package/dist/mock/monorise/organization.js.map +0 -1
  191. package/dist/mock/monorise/reference.d.ts +0 -171
  192. package/dist/mock/monorise/reference.d.ts.map +0 -1
  193. package/dist/mock/monorise/reference.js +0 -25
  194. package/dist/mock/monorise/reference.js.map +0 -1
  195. package/dist/mock/monorise/video.d.ts +0 -346
  196. package/dist/mock/monorise/video.d.ts.map +0 -1
  197. package/dist/mock/monorise/video.js +0 -33
  198. package/dist/mock/monorise/video.js.map +0 -1
@@ -1,16 +1,21 @@
1
- import { ConditionalCheckFailedException, } from '@aws-sdk/client-dynamodb';
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { BatchStatementErrorCodeEnum, ConditionalCheckFailedException, TransactionCanceledException, } from '@aws-sdk/client-dynamodb';
2
11
  import { marshall, unmarshall } from '@aws-sdk/util-dynamodb';
3
12
  import { ulid } from 'ulid';
4
- import { StandardError } from '../errors/standard-error';
13
+ import { StandardError, StandardErrorCode } from '../errors/standard-error';
14
+ import { fromLastKeyQuery } from '../helpers/fromLastKeyQuery';
15
+ import { toLastKeyResponse } from '../helpers/toLastKeyResponse';
5
16
  import { Item } from './abstract/Item.base';
6
17
  import { Repository } from './abstract/Repository.base';
7
18
  export class Entity extends Item {
8
- entityType;
9
- entityId;
10
- data;
11
- _createdAt;
12
- _updatedAt;
13
- fullId;
14
19
  constructor(entityType, entityId, data = {}, _createdAt, _updatedAt) {
15
20
  super();
16
21
  this.entityType = entityType;
@@ -22,7 +27,7 @@ export class Entity extends Item {
22
27
  }
23
28
  static fromItem(item) {
24
29
  if (!item)
25
- throw new StandardError('ENTITY_IS_UNDEFINED', 'Entity item empty');
30
+ throw new StandardError(StandardErrorCode.ENTITY_IS_UNDEFINED, 'Entity item empty');
26
31
  const parsedItem = unmarshall(item);
27
32
  return new Entity(parsedItem.entityType, parsedItem.entityId, parsedItem.data, parsedItem.createdAt ? new Date(parsedItem.createdAt) : undefined, parsedItem.updatedAt ? new Date(parsedItem.updatedAt) : undefined);
28
33
  }
@@ -46,16 +51,15 @@ export class Entity extends Item {
46
51
  };
47
52
  }
48
53
  get createdAt() {
49
- return this._createdAt?.toISOString();
54
+ var _a;
55
+ return (_a = this._createdAt) === null || _a === void 0 ? void 0 : _a.toISOString();
50
56
  }
51
57
  get updatedAt() {
52
- return this._updatedAt?.toISOString();
58
+ var _a;
59
+ return (_a = this._updatedAt) === null || _a === void 0 ? void 0 : _a.toISOString();
53
60
  }
54
61
  toItem() {
55
- return {
56
- ...marshall(this.toJSON(), { removeUndefinedValues: true }),
57
- ...this.keys(),
58
- };
62
+ return Object.assign(Object.assign({}, marshall(this.toJSON(), { removeUndefinedValues: true })), this.keys());
59
63
  }
60
64
  toJSON() {
61
65
  return {
@@ -68,10 +72,6 @@ export class Entity extends Item {
68
72
  }
69
73
  }
70
74
  export class EntityRepository extends Repository {
71
- EntityConfig;
72
- TABLE_NAME;
73
- dynamodbClient;
74
- EmailAuthEnabledEntities;
75
75
  constructor(EntityConfig, TABLE_NAME, dynamodbClient, EmailAuthEnabledEntities) {
76
76
  super();
77
77
  this.EntityConfig = EntityConfig;
@@ -79,114 +79,154 @@ export class EntityRepository extends Repository {
79
79
  this.dynamodbClient = dynamodbClient;
80
80
  this.EmailAuthEnabledEntities = EmailAuthEnabledEntities;
81
81
  }
82
- async listEntities({ entityType, limit, // if this is not set, it will return all items
83
- between, options = {}, }) {
84
- const entity = new Entity(entityType);
85
- // when query for records that SK are between provided start and end
86
- const expression = between
87
- ? {
88
- KeyConditionExpression: '#PK = :PK and #SK between :SKStart and :SKEnd',
82
+ listEntities(_a) {
83
+ return __awaiter(this, arguments, void 0, function* ({ entityType, limit, // if this is not set, it will return all items
84
+ between, options = {}, }) {
85
+ var _b, _c, _d;
86
+ const entity = new Entity(entityType);
87
+ // when query for records that SK are between provided start and end
88
+ const expression = between
89
+ ? {
90
+ KeyConditionExpression: '#PK = :PK and #SK between :SKStart and :SKEnd',
91
+ ExpressionAttributeNames: {
92
+ '#PK': 'PK',
93
+ '#SK': 'SK',
94
+ },
95
+ ExpressionAttributeValues: {
96
+ ':PK': {
97
+ S: entity.listActionKey,
98
+ },
99
+ ':SKStart': {
100
+ S: `${entityType}#${between.start}`,
101
+ },
102
+ ':SKEnd': {
103
+ S: `${entityType}#${between.end}`,
104
+ },
105
+ },
106
+ }
107
+ : {
108
+ KeyConditionExpression: '#PK = :PK',
109
+ ExpressionAttributeNames: {
110
+ '#PK': 'PK',
111
+ },
112
+ ExpressionAttributeValues: {
113
+ ':PK': {
114
+ S: entity.listActionKey,
115
+ },
116
+ },
117
+ };
118
+ const defaultListQuery = Object.assign({ TableName: this.TABLE_NAME, Limit: limit, ScanIndexForward: false, ProjectionExpression: options === null || options === void 0 ? void 0 : options.ProjectionExpression }, expression);
119
+ let lastKey = options.lastKey;
120
+ let items = [];
121
+ let remainingCount = limit !== null && limit !== void 0 ? limit : 0;
122
+ do {
123
+ const resp = yield this.dynamodbClient.query(Object.assign(Object.assign(Object.assign({}, defaultListQuery), (remainingCount && { Limit: remainingCount })), (lastKey && {
124
+ ExclusiveStartKey: fromLastKeyQuery(lastKey),
125
+ })));
126
+ items = items.concat((_b = resp.Items) !== null && _b !== void 0 ? _b : []);
127
+ lastKey = toLastKeyResponse(resp.LastEvaluatedKey);
128
+ if (limit) {
129
+ remainingCount = remainingCount - ((_d = (_c = resp.Items) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0);
130
+ }
131
+ } while (
132
+ // limit is given, haven't reach limit, and there are still items to retrieve
133
+ (limit && remainingCount && lastKey) ||
134
+ // no limit is given and there are still items to retrieve
135
+ (!limit && lastKey));
136
+ return {
137
+ items: (items || []).map((Entity.fromItem)),
138
+ totalCount: items.length,
139
+ lastKey,
140
+ };
141
+ });
142
+ }
143
+ getEntity(entityType, entityId) {
144
+ return __awaiter(this, void 0, void 0, function* () {
145
+ const entity = new Entity(entityType, entityId);
146
+ const resp = yield this.dynamodbClient.getItem({
147
+ TableName: this.TABLE_NAME,
148
+ Key: entity.keys(),
149
+ });
150
+ return Entity.fromItem(resp.Item);
151
+ });
152
+ }
153
+ getEntityByEmail(entityType, email) {
154
+ return __awaiter(this, void 0, void 0, function* () {
155
+ var _a;
156
+ const resp = yield this.dynamodbClient.query({
157
+ TableName: this.TABLE_NAME,
158
+ KeyConditionExpression: '#PK = :PK and begins_with(#SK, :SK)',
89
159
  ExpressionAttributeNames: {
90
160
  '#PK': 'PK',
91
161
  '#SK': 'SK',
92
162
  },
93
163
  ExpressionAttributeValues: {
94
- ':PK': {
95
- S: entity.listActionKey,
96
- },
97
- ':SKStart': {
98
- S: `${entityType}#${between.start}`,
99
- },
100
- ':SKEnd': {
101
- S: `${entityType}#${between.end}`,
102
- },
164
+ ':PK': { S: `EMAIL#${email}` },
165
+ ':SK': { S: entityType },
103
166
  },
104
- }
105
- : {
106
- KeyConditionExpression: '#PK = :PK',
167
+ });
168
+ return Entity.fromItem((_a = resp.Items) === null || _a === void 0 ? void 0 : _a[0]);
169
+ });
170
+ }
171
+ getEmailAvailability(entityType, email) {
172
+ return __awaiter(this, void 0, void 0, function* () {
173
+ var _a;
174
+ const resp = yield this.dynamodbClient.query({
175
+ TableName: this.TABLE_NAME,
176
+ KeyConditionExpression: '#PK = :PK and begins_with(#SK, :SK)',
107
177
  ExpressionAttributeNames: {
108
178
  '#PK': 'PK',
179
+ '#SK': 'SK',
109
180
  },
110
181
  ExpressionAttributeValues: {
111
- ':PK': {
112
- S: entity.listActionKey,
113
- },
182
+ ':PK': { S: `EMAIL#${email}` },
183
+ ':SK': { S: entityType },
114
184
  },
115
- };
116
- const defaultListQuery = {
117
- TableName: this.TABLE_NAME,
118
- Limit: limit,
119
- ScanIndexForward: false,
120
- ProjectionExpression: options?.ProjectionExpression,
121
- ...expression,
122
- };
123
- let lastKey = options.lastKey;
124
- let items = [];
125
- let remainingCount = limit ?? 0;
126
- do {
127
- const resp = await this.dynamodbClient.query({
128
- ...defaultListQuery,
129
- ...(remainingCount && { Limit: remainingCount }),
130
- ...(lastKey && {
131
- ExclusiveStartKey: lastKey,
132
- }),
133
185
  });
134
- items = items.concat(resp.Items ?? []);
135
- lastKey = resp.LastEvaluatedKey;
136
- if (limit) {
137
- remainingCount = remainingCount - (resp.Items?.length ?? 0);
186
+ if ((_a = resp.Items) === null || _a === void 0 ? void 0 : _a[0]) {
187
+ throw new StandardError(StandardErrorCode.EMAIL_EXISTS, 'Email already exists');
138
188
  }
139
- } while (
140
- // limit is given, haven't reach limit, and there are still items to retrieve
141
- (limit && remainingCount && lastKey) ||
142
- // no limit is given and there are still items to retrieve
143
- (!limit && lastKey));
144
- return {
145
- items: (items || []).map((Entity.fromItem)),
146
- totalCount: items.length,
147
- lastKey,
148
- };
149
- }
150
- async getEntity(entityType, entityId) {
151
- const entity = new Entity(entityType, entityId);
152
- const resp = await this.dynamodbClient.getItem({
153
- TableName: this.TABLE_NAME,
154
- Key: entity.keys(),
189
+ return;
155
190
  });
156
- return Entity.fromItem(resp.Item);
157
191
  }
158
- async getEntityByEmail(entityType, email) {
159
- const resp = await this.dynamodbClient.query({
160
- TableName: this.TABLE_NAME,
161
- KeyConditionExpression: '#PK = :PK and begins_with(#SK, :SK)',
162
- ExpressionAttributeNames: {
163
- '#PK': 'PK',
164
- '#SK': 'SK',
165
- },
166
- ExpressionAttributeValues: {
167
- ':PK': { S: `EMAIL#${email}` },
168
- ':SK': { S: entityType },
169
- },
192
+ getEntityByUniqueField(entityType, fieldName, value) {
193
+ return __awaiter(this, void 0, void 0, function* () {
194
+ var _a;
195
+ const resp = yield this.dynamodbClient.query({
196
+ TableName: this.TABLE_NAME,
197
+ KeyConditionExpression: '#PK = :PK and begins_with(#SK, :SK)',
198
+ ExpressionAttributeNames: {
199
+ '#PK': 'PK',
200
+ '#SK': 'SK',
201
+ },
202
+ ExpressionAttributeValues: {
203
+ ':PK': { S: `UNIQUE#${fieldName}#${value}` },
204
+ ':SK': { S: entityType },
205
+ },
206
+ });
207
+ return Entity.fromItem((_a = resp.Items) === null || _a === void 0 ? void 0 : _a[0]);
170
208
  });
171
- return Entity.fromItem(resp.Items?.[0]);
172
209
  }
173
- async getEmailAvailability(entityType, email) {
174
- const resp = await this.dynamodbClient.query({
175
- TableName: this.TABLE_NAME,
176
- KeyConditionExpression: '#PK = :PK and begins_with(#SK, :SK)',
177
- ExpressionAttributeNames: {
178
- '#PK': 'PK',
179
- '#SK': 'SK',
180
- },
181
- ExpressionAttributeValues: {
182
- ':PK': { S: `EMAIL#${email}` },
183
- ':SK': { S: entityType },
184
- },
210
+ getUniqueFieldValueAvailability(entityType, fieldName, value) {
211
+ return __awaiter(this, void 0, void 0, function* () {
212
+ var _a;
213
+ const resp = yield this.dynamodbClient.query({
214
+ TableName: this.TABLE_NAME,
215
+ KeyConditionExpression: '#PK = :PK and begins_with(#SK, :SK)',
216
+ ExpressionAttributeNames: {
217
+ '#PK': 'PK',
218
+ '#SK': 'SK',
219
+ },
220
+ ExpressionAttributeValues: {
221
+ ':PK': { S: `UNIQUE#${fieldName}#${value}` },
222
+ ':SK': { S: entityType },
223
+ },
224
+ });
225
+ if ((_a = resp.Items) === null || _a === void 0 ? void 0 : _a[0]) {
226
+ throw new StandardError(StandardErrorCode.UNIQUE_VALUE_EXISTS, `${fieldName} '${value}' already exists`);
227
+ }
228
+ return;
185
229
  });
186
- if (resp.Items?.[0]) {
187
- throw new StandardError('EMAIL_EXISTS', 'Email already exists');
188
- }
189
- return;
190
230
  }
191
231
  createEntityTransactItems(entity, opts) {
192
232
  const TransactItems = [
@@ -194,26 +234,17 @@ export class EntityRepository extends Repository {
194
234
  Put: {
195
235
  TableName: this.TABLE_NAME,
196
236
  ConditionExpression: 'attribute_not_exists(PK)',
197
- Item: {
198
- ...entity.toItem(),
199
- ...(opts?.mutualId && {
200
- R2PK: { S: opts.mutualId },
201
- R2SK: { S: entity.pk },
202
- }),
203
- },
237
+ Item: Object.assign(Object.assign({}, entity.toItem()), ((opts === null || opts === void 0 ? void 0 : opts.mutualId) && {
238
+ R2PK: { S: opts.mutualId },
239
+ R2SK: { S: entity.pk },
240
+ })),
204
241
  },
205
242
  },
206
243
  {
207
244
  Put: {
208
245
  TableName: this.TABLE_NAME,
209
246
  ConditionExpression: 'attribute_not_exists(PK)',
210
- Item: {
211
- ...entity.toItem(),
212
- PK: { S: entity.listActionKey },
213
- SK: entity.keys().PK,
214
- R1PK: entity.keys().PK,
215
- R1SK: { S: entity.listActionKey },
216
- },
247
+ Item: Object.assign(Object.assign({}, entity.toItem()), { PK: { S: entity.listActionKey }, SK: entity.keys().PK, R1PK: entity.keys().PK, R1SK: { S: entity.listActionKey } }),
217
248
  },
218
249
  },
219
250
  ];
@@ -226,136 +257,248 @@ export class EntityRepository extends Repository {
226
257
  Put: {
227
258
  TableName: this.TABLE_NAME,
228
259
  ConditionExpression: 'attribute_not_exists(PK)',
229
- Item: {
230
- ...entity.toItem(),
231
- ...entity.emailKeys,
232
- R1PK: entity.emailKeys.SK,
233
- R1SK: entity.emailKeys.PK,
234
- },
260
+ Item: Object.assign(Object.assign(Object.assign({}, entity.toItem()), entity.emailKeys), { R1PK: entity.emailKeys.SK, R1SK: entity.emailKeys.PK }),
261
+ },
262
+ });
263
+ }
264
+ for (const field in opts.uniqueFieldValues || {}) {
265
+ TransactItems.push({
266
+ Put: {
267
+ TableName: this.TABLE_NAME,
268
+ ConditionExpression: 'attribute_not_exists(PK)',
269
+ Item: Object.assign(Object.assign({}, entity.toItem()), { PK: {
270
+ S: `UNIQUE#${field}#${entity.data[field]}`,
271
+ }, SK: { S: entity.entityType }, R1PK: entity.keys().PK, R1SK: entity.keys().SK, createdAt: { S: entity.createdAt || new Date().toISOString() }, updatedAt: { S: entity.updatedAt || new Date().toISOString() } }),
235
272
  },
236
273
  });
237
274
  }
238
275
  return TransactItems;
239
276
  }
240
- async createEntity(entityType, entityPayload, entityId, opts) {
241
- const currentDatetime = opts?.createAndUpdateDatetime ?? new Date();
242
- const entity = new Entity(entityType, entityId || ulid(), entityPayload, currentDatetime, currentDatetime);
243
- const TransactItems = this.createEntityTransactItems(entity, {
244
- mutualId: opts?.mutualId,
245
- });
246
- await this.dynamodbClient.transactWriteItems({ TransactItems });
247
- return entity;
248
- }
249
- async upsertEntity(entityType, entityId, payload) {
250
- const toUpdateExpressions = this.toUpdate({
251
- entityType,
252
- entityId,
253
- data: payload,
277
+ createEntity(entityType, entityPayload, entityId, opts) {
278
+ return __awaiter(this, void 0, void 0, function* () {
279
+ var _a, _b;
280
+ const currentDatetime = (_a = opts === null || opts === void 0 ? void 0 : opts.createAndUpdateDatetime) !== null && _a !== void 0 ? _a : new Date();
281
+ const entity = new Entity(entityType, entityId || ulid(), entityPayload, currentDatetime, currentDatetime);
282
+ const uniqueFields = (this.EntityConfig[entityType].uniqueFields ||
283
+ []);
284
+ const uniqueFieldValues = {};
285
+ for (const field of uniqueFields) {
286
+ if (!(field in entityPayload))
287
+ continue;
288
+ if (typeof entityPayload[field] !== 'string') {
289
+ throw new StandardError(StandardErrorCode.INVALID_UNIQUE_VALUE_TYPE, `Invalid type. ${field} is not a 'string'.`);
290
+ }
291
+ uniqueFieldValues[field] = entityPayload[field];
292
+ }
293
+ const TransactItems = this.createEntityTransactItems(entity, {
294
+ mutualId: opts === null || opts === void 0 ? void 0 : opts.mutualId,
295
+ uniqueFieldValues,
296
+ });
297
+ try {
298
+ yield this.dynamodbClient.transactWriteItems({ TransactItems });
299
+ }
300
+ catch (err) {
301
+ if (err instanceof TransactionCanceledException) {
302
+ let idxUniqueFieldErr = 2;
303
+ if (this.EmailAuthEnabledEntities.includes(entity.entityType)) {
304
+ idxUniqueFieldErr += 1;
305
+ }
306
+ const uniqueFieldErr = ((_b = err.CancellationReasons) === null || _b === void 0 ? void 0 : _b.slice(idxUniqueFieldErr)) || [];
307
+ for (let i = 0; i < uniqueFieldErr.length; i++) {
308
+ if (uniqueFieldErr[i].Code ===
309
+ BatchStatementErrorCodeEnum.ConditionalCheckFailed) {
310
+ const field = uniqueFields[i];
311
+ throw new StandardError(StandardErrorCode.UNIQUE_VALUE_EXISTS, `${field} '${uniqueFieldValues[field]}' already exists`);
312
+ }
313
+ }
314
+ }
315
+ throw err;
316
+ }
317
+ return entity;
254
318
  });
255
- const params = {
256
- TableName: this.TABLE_NAME,
257
- ReturnValues: 'ALL_NEW',
258
- Key: new Entity(entityType, entityId).keys(),
259
- UpdateExpression: toUpdateExpressions.UpdateExpression,
260
- ExpressionAttributeNames: {
261
- ...toUpdateExpressions.ExpressionAttributeNames,
262
- },
263
- ExpressionAttributeValues: {
264
- ...toUpdateExpressions.ExpressionAttributeValues,
265
- },
266
- };
267
- const resp = await this.dynamodbClient.updateItem(params);
268
- const updatedEntity = Entity.fromItem(resp.Attributes);
269
- return updatedEntity;
270
319
  }
271
- async updateEntity(entityType, entityId, toUpdate, opts) {
272
- try {
320
+ upsertEntity(entityType, entityId, payload) {
321
+ return __awaiter(this, void 0, void 0, function* () {
273
322
  const currentDatetime = new Date().toISOString();
274
323
  const toUpdateExpressions = this.toUpdate({
324
+ entityType,
325
+ entityId,
326
+ data: payload,
275
327
  updatedAt: currentDatetime,
276
- ...toUpdate,
277
328
  });
278
329
  const params = {
279
330
  TableName: this.TABLE_NAME,
280
331
  ReturnValues: 'ALL_NEW',
281
332
  Key: new Entity(entityType, entityId).keys(),
282
- ConditionExpression: opts?.ConditionExpression || 'attribute_exists(PK)',
283
333
  UpdateExpression: toUpdateExpressions.UpdateExpression,
284
- ExpressionAttributeNames: {
285
- ...toUpdateExpressions.ExpressionAttributeNames,
286
- ...opts?.ExpressionAttributeNames,
287
- },
288
- ExpressionAttributeValues: {
289
- ...toUpdateExpressions.ExpressionAttributeValues,
290
- ...opts?.ExpressionAttributeValues,
291
- },
334
+ ExpressionAttributeNames: Object.assign({}, toUpdateExpressions.ExpressionAttributeNames),
335
+ ExpressionAttributeValues: Object.assign({}, toUpdateExpressions.ExpressionAttributeValues),
292
336
  };
293
- const resp = await this.dynamodbClient.updateItem(params);
337
+ const resp = yield this.dynamodbClient.updateItem(params);
294
338
  const updatedEntity = Entity.fromItem(resp.Attributes);
295
339
  return updatedEntity;
296
- }
297
- catch (err) {
298
- if (err instanceof ConditionalCheckFailedException) {
299
- throw new StandardError('ENTITY_NOT_FOUND', 'Entity not found', err, {
300
- entityId,
301
- toUpdate,
302
- });
303
- }
304
- throw err;
305
- }
340
+ });
306
341
  }
307
- async deleteEntity(entityType, entityId) {
308
- try {
309
- const entity = new Entity(entityType, entityId);
310
- await this.dynamodbClient.deleteItem({
311
- TableName: this.TABLE_NAME,
312
- Key: entity.keys(),
313
- ConditionExpression: 'attribute_exists(PK)',
342
+ updateEntityTransactItems(entity, updateParams, previousUniqueFieldValues, previousEntity) {
343
+ const transactItems = [
344
+ {
345
+ Update: Object.assign(Object.assign({}, updateParams), { UpdateExpression: updateParams.UpdateExpression }),
346
+ },
347
+ ];
348
+ for (const field in previousUniqueFieldValues) {
349
+ transactItems.push({
350
+ Delete: {
351
+ TableName: this.TABLE_NAME,
352
+ Key: {
353
+ PK: { S: `UNIQUE#${field}#${previousUniqueFieldValues[field]}` },
354
+ SK: { S: entity.entityType },
355
+ },
356
+ },
357
+ }, {
358
+ Put: {
359
+ TableName: this.TABLE_NAME,
360
+ ConditionExpression: 'attribute_not_exists(PK)',
361
+ Item: Object.assign(Object.assign({}, entity.toItem()), { data: {
362
+ M: Object.assign(Object.assign({}, previousEntity.toItem().data.M), entity.toItem().data.M),
363
+ }, PK: {
364
+ S: `UNIQUE#${field}#${entity.data[field]}`,
365
+ }, SK: { S: entity.entityType }, R1PK: entity.keys().PK, R1SK: entity.keys().SK, createdAt: {
366
+ S: previousEntity.createdAt ||
367
+ entity.createdAt ||
368
+ new Date().toISOString(),
369
+ }, updatedAt: { S: entity.updatedAt || new Date().toISOString() } }),
370
+ },
314
371
  });
315
372
  }
316
- catch (err) {
317
- if (err instanceof ConditionalCheckFailedException) {
318
- throw new StandardError('ENTITY_NOT_FOUND', 'Entity not found', err, {
319
- entityId,
320
- });
373
+ return transactItems;
374
+ }
375
+ updateEntity(entityType, entityId, toUpdate, opts) {
376
+ return __awaiter(this, void 0, void 0, function* () {
377
+ try {
378
+ const currentDatetime = new Date().toISOString();
379
+ const toUpdateExpressions = this.toUpdate(Object.assign({ updatedAt: currentDatetime }, toUpdate));
380
+ const params = {
381
+ TableName: this.TABLE_NAME,
382
+ ReturnValues: 'ALL_NEW',
383
+ Key: new Entity(entityType, entityId).keys(),
384
+ ConditionExpression: (opts === null || opts === void 0 ? void 0 : opts.ConditionExpression) || 'attribute_exists(PK)',
385
+ UpdateExpression: toUpdateExpressions.UpdateExpression,
386
+ ExpressionAttributeNames: Object.assign(Object.assign({}, toUpdateExpressions.ExpressionAttributeNames), opts === null || opts === void 0 ? void 0 : opts.ExpressionAttributeNames),
387
+ ExpressionAttributeValues: Object.assign(Object.assign({}, toUpdateExpressions.ExpressionAttributeValues), opts === null || opts === void 0 ? void 0 : opts.ExpressionAttributeValues),
388
+ };
389
+ const entity = new Entity(entityType, entityId, toUpdate.data);
390
+ const uniqueFields = (this.EntityConfig[entityType].uniqueFields ||
391
+ []);
392
+ const hasUniqueFields = Object.keys(toUpdate.data).some((field) => uniqueFields.includes(field));
393
+ let updatedUniqueFields = [];
394
+ let previousUniqueFieldValues = {};
395
+ let previousEntity;
396
+ if (hasUniqueFields) {
397
+ previousEntity = yield this.getEntity(entityType, entityId);
398
+ // check if any of the unique fields has changed
399
+ updatedUniqueFields = uniqueFields.filter((field) => toUpdate.data[field] !== undefined &&
400
+ toUpdate.data[field] !==
401
+ previousEntity.data[field]);
402
+ previousUniqueFieldValues = updatedUniqueFields.reduce((acc, field) => (Object.assign(Object.assign({}, acc), { [field]: previousEntity.data[field] })), {});
403
+ for (const field in previousUniqueFieldValues) {
404
+ if (typeof toUpdate.data[field] !==
405
+ 'string') {
406
+ throw new StandardError(StandardErrorCode.INVALID_UNIQUE_VALUE_TYPE, `Invalid type. ${field} is not a 'string'.`);
407
+ }
408
+ }
409
+ if (updatedUniqueFields.length > 0) {
410
+ const TransactItems = this.updateEntityTransactItems(entity, params, previousUniqueFieldValues, previousEntity);
411
+ try {
412
+ yield this.dynamodbClient.transactWriteItems({ TransactItems });
413
+ }
414
+ catch (err) {
415
+ if (err instanceof TransactionCanceledException) {
416
+ const [updateErr, ...uniqueFieldErr] = err.CancellationReasons || [];
417
+ for (let i = 0; i < uniqueFieldErr.length; i++) {
418
+ if (i % 2 === 1 &&
419
+ uniqueFieldErr[i].Code ===
420
+ BatchStatementErrorCodeEnum.ConditionalCheckFailed) {
421
+ const field = updatedUniqueFields[Math.floor(i / 2)];
422
+ throw new StandardError(StandardErrorCode.UNIQUE_VALUE_EXISTS, `${field} '${entity.data[field]}' already exists`);
423
+ }
424
+ }
425
+ }
426
+ throw err;
427
+ }
428
+ return yield this.getEntity(entityType, entityId);
429
+ }
430
+ }
431
+ const resp = yield this.dynamodbClient.updateItem(params);
432
+ const updatedEntity = Entity.fromItem(resp.Attributes);
433
+ return updatedEntity;
321
434
  }
322
- throw err;
323
- }
435
+ catch (err) {
436
+ if (err instanceof ConditionalCheckFailedException) {
437
+ throw new StandardError(StandardErrorCode.ENTITY_NOT_FOUND, 'Entity not found', err, {
438
+ entityId,
439
+ toUpdate,
440
+ });
441
+ }
442
+ throw err;
443
+ }
444
+ });
324
445
  }
325
- async queryEntities(entityType, query) {
326
- const results = {
327
- items: [],
328
- totalCount: 0,
329
- };
330
- // let regex be empty if its invalid (eg. +)
331
- let queryRegex = /(?:)/;
332
- try {
333
- queryRegex = new RegExp(query.toLowerCase());
334
- }
335
- catch (err) {
336
- return results;
337
- }
338
- const listResults = await this.listEntities({
339
- entityType,
446
+ deleteEntity(entityType, entityId) {
447
+ return __awaiter(this, void 0, void 0, function* () {
448
+ try {
449
+ const entity = new Entity(entityType, entityId);
450
+ yield this.dynamodbClient.deleteItem({
451
+ TableName: this.TABLE_NAME,
452
+ Key: entity.keys(),
453
+ ConditionExpression: 'attribute_exists(PK)',
454
+ });
455
+ }
456
+ catch (err) {
457
+ if (err instanceof ConditionalCheckFailedException) {
458
+ throw new StandardError(StandardErrorCode.ENTITY_NOT_FOUND, 'Entity not found', err, {
459
+ entityId,
460
+ });
461
+ }
462
+ throw err;
463
+ }
340
464
  });
341
- results.items.push(...listResults.items);
342
- results.totalCount += listResults.totalCount || 0;
343
- const filteredItems = [];
344
- const { searchableFields } = this.EntityConfig[entityType];
345
- for (const item of results.items) {
346
- const searchTerm = (searchableFields ?? [])
347
- .map((field) => item.data[field]?.toLowerCase())
348
- .join(' ');
349
- const isMatched = queryRegex.test(searchTerm);
350
- if (isMatched) {
351
- filteredItems.push(item);
465
+ }
466
+ queryEntities(entityType, query) {
467
+ return __awaiter(this, void 0, void 0, function* () {
468
+ const results = {
469
+ items: [],
470
+ totalCount: 0,
471
+ };
472
+ // let regex be empty if its invalid (eg. +)
473
+ let queryRegex = /(?:)/;
474
+ try {
475
+ queryRegex = new RegExp(query.toLowerCase());
352
476
  }
353
- }
354
- return {
355
- items: filteredItems,
356
- totalCount: results.totalCount,
357
- filteredCount: filteredItems.length,
358
- };
477
+ catch (err) {
478
+ return results;
479
+ }
480
+ const listResults = yield this.listEntities({
481
+ entityType,
482
+ });
483
+ results.items.push(...listResults.items);
484
+ results.totalCount += listResults.totalCount || 0;
485
+ const filteredItems = [];
486
+ const { searchableFields } = this.EntityConfig[entityType];
487
+ for (const item of results.items) {
488
+ const searchTerm = (searchableFields !== null && searchableFields !== void 0 ? searchableFields : [])
489
+ .map((field) => { var _a; return (_a = item.data[field]) === null || _a === void 0 ? void 0 : _a.toLowerCase(); })
490
+ .join(' ');
491
+ const isMatched = queryRegex.test(searchTerm);
492
+ if (isMatched) {
493
+ filteredItems.push(item);
494
+ }
495
+ }
496
+ return {
497
+ items: filteredItems,
498
+ totalCount: results.totalCount,
499
+ filteredCount: filteredItems.length,
500
+ };
501
+ });
359
502
  }
360
503
  }
361
504
  //# sourceMappingURL=Entity.js.map