dyna-record 0.1.5 → 0.2.1

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 (112) hide show
  1. package/README.md +11 -13
  2. package/dist/src/DynaRecord.d.ts +36 -28
  3. package/dist/src/DynaRecord.d.ts.map +1 -1
  4. package/dist/src/DynaRecord.js +39 -36
  5. package/dist/src/decorators/Table.d.ts.map +1 -1
  6. package/dist/src/decorators/attributes/BooleanAttribute.d.ts.map +1 -1
  7. package/dist/src/decorators/attributes/DateAttribute.d.ts.map +1 -1
  8. package/dist/src/decorators/attributes/EnumAttribute.d.ts.map +1 -1
  9. package/dist/src/decorators/attributes/ForeignKeyAttribute.d.ts.map +1 -1
  10. package/dist/src/decorators/attributes/NumberAttribute.d.ts.map +1 -1
  11. package/dist/src/decorators/attributes/PartitionKeyAttribute.d.ts.map +1 -1
  12. package/dist/src/decorators/attributes/SortKeyAttribute.d.ts.map +1 -1
  13. package/dist/src/decorators/attributes/StringAttribute.d.ts.map +1 -1
  14. package/dist/src/decorators/attributes/serializers.d.ts +1 -1
  15. package/dist/src/decorators/relationships/BelongsTo.d.ts.map +1 -1
  16. package/dist/src/decorators/relationships/HasAndBelongsToMany.d.ts.map +1 -1
  17. package/dist/src/decorators/relationships/HasMany.d.ts +7 -0
  18. package/dist/src/decorators/relationships/HasMany.d.ts.map +1 -1
  19. package/dist/src/decorators/relationships/HasMany.js +12 -2
  20. package/dist/src/decorators/relationships/HasOne.d.ts.map +1 -1
  21. package/dist/src/decorators/types.d.ts +2 -2
  22. package/dist/src/decorators/types.d.ts.map +1 -1
  23. package/dist/src/dynamo-utils/TransactGetBuilder.d.ts +4 -0
  24. package/dist/src/dynamo-utils/TransactGetBuilder.d.ts.map +1 -1
  25. package/dist/src/dynamo-utils/TransactGetBuilder.js +6 -0
  26. package/dist/src/metadata/EntityMetadata.d.ts +21 -1
  27. package/dist/src/metadata/EntityMetadata.d.ts.map +1 -1
  28. package/dist/src/metadata/EntityMetadata.js +33 -0
  29. package/dist/src/metadata/TableMetadata.d.ts.map +1 -1
  30. package/dist/src/metadata/TableMetadata.js +1 -3
  31. package/dist/src/metadata/relationship-metadata/BelongsToRelationship.d.ts +1 -1
  32. package/dist/src/metadata/relationship-metadata/BelongsToRelationship.js +1 -1
  33. package/dist/src/metadata/relationship-metadata/HasManyRelationship.d.ts +2 -1
  34. package/dist/src/metadata/relationship-metadata/HasManyRelationship.d.ts.map +1 -1
  35. package/dist/src/metadata/relationship-metadata/HasManyRelationship.js +2 -1
  36. package/dist/src/metadata/relationship-metadata/OwnedByRelationship.d.ts +18 -0
  37. package/dist/src/metadata/relationship-metadata/OwnedByRelationship.d.ts.map +1 -0
  38. package/dist/src/metadata/relationship-metadata/OwnedByRelationship.js +26 -0
  39. package/dist/src/metadata/relationship-metadata/RelationshipMetadata.d.ts +1 -1
  40. package/dist/src/metadata/relationship-metadata/RelationshipMetadata.d.ts.map +1 -1
  41. package/dist/src/metadata/relationship-metadata/index.d.ts +2 -1
  42. package/dist/src/metadata/relationship-metadata/index.d.ts.map +1 -1
  43. package/dist/src/metadata/relationship-metadata/index.js +3 -1
  44. package/dist/src/metadata/relationship-metadata/types.d.ts +7 -2
  45. package/dist/src/metadata/relationship-metadata/types.d.ts.map +1 -1
  46. package/dist/src/metadata/relationship-metadata/utils.d.ts.map +1 -1
  47. package/dist/src/metadata/relationship-metadata/utils.js +3 -0
  48. package/dist/src/metadata/types.d.ts +7 -4
  49. package/dist/src/metadata/types.d.ts.map +1 -1
  50. package/dist/src/metadata/utils.d.ts +8 -4
  51. package/dist/src/metadata/utils.d.ts.map +1 -1
  52. package/dist/src/metadata/utils.js +8 -1
  53. package/dist/src/operations/Create/Create.d.ts +95 -16
  54. package/dist/src/operations/Create/Create.d.ts.map +1 -1
  55. package/dist/src/operations/Create/Create.js +171 -22
  56. package/dist/src/operations/Delete/Delete.d.ts +32 -23
  57. package/dist/src/operations/Delete/Delete.d.ts.map +1 -1
  58. package/dist/src/operations/Delete/Delete.js +117 -100
  59. package/dist/src/operations/Delete/types.d.ts +0 -3
  60. package/dist/src/operations/Delete/types.d.ts.map +1 -1
  61. package/dist/src/operations/FindById/FindById.d.ts +7 -28
  62. package/dist/src/operations/FindById/FindById.d.ts.map +1 -1
  63. package/dist/src/operations/FindById/FindById.js +36 -125
  64. package/dist/src/operations/FindById/types.d.ts +8 -9
  65. package/dist/src/operations/FindById/types.d.ts.map +1 -1
  66. package/dist/src/operations/Query/Query.d.ts +2 -1
  67. package/dist/src/operations/Query/Query.d.ts.map +1 -1
  68. package/dist/src/operations/Query/Query.js +17 -4
  69. package/dist/src/operations/Query/types.d.ts +39 -5
  70. package/dist/src/operations/Query/types.d.ts.map +1 -1
  71. package/dist/src/operations/Update/Update.d.ts +185 -24
  72. package/dist/src/operations/Update/Update.d.ts.map +1 -1
  73. package/dist/src/operations/Update/Update.js +409 -72
  74. package/dist/src/operations/Update/UpdateDryRun.d.ts +10 -0
  75. package/dist/src/operations/Update/UpdateDryRun.d.ts.map +1 -0
  76. package/dist/src/operations/Update/UpdateDryRun.js +15 -0
  77. package/dist/src/operations/Update/index.d.ts +1 -0
  78. package/dist/src/operations/Update/index.d.ts.map +1 -1
  79. package/dist/src/operations/Update/index.js +3 -1
  80. package/dist/src/operations/types.d.ts +6 -1
  81. package/dist/src/operations/types.d.ts.map +1 -1
  82. package/dist/src/operations/utils/expressionBuilder.d.ts.map +1 -1
  83. package/dist/src/operations/utils/expressionBuilder.js +1 -4
  84. package/dist/src/operations/utils/index.d.ts +0 -1
  85. package/dist/src/operations/utils/index.d.ts.map +1 -1
  86. package/dist/src/operations/utils/index.js +0 -6
  87. package/dist/src/operations/utils/utils.d.ts +13 -3
  88. package/dist/src/operations/utils/utils.d.ts.map +1 -1
  89. package/dist/src/operations/utils/utils.js +33 -3
  90. package/dist/src/query-utils/Filters.d.ts +1 -1
  91. package/dist/src/query-utils/Filters.d.ts.map +1 -1
  92. package/dist/src/query-utils/Filters.js +7 -15
  93. package/dist/src/query-utils/QueryBuilder.d.ts.map +1 -1
  94. package/dist/src/query-utils/QueryBuilder.js +9 -1
  95. package/dist/src/relationships/JoinTable.d.ts +17 -7
  96. package/dist/src/relationships/JoinTable.d.ts.map +1 -1
  97. package/dist/src/relationships/JoinTable.js +77 -20
  98. package/dist/src/relationships/index.d.ts +0 -1
  99. package/dist/src/relationships/index.d.ts.map +1 -1
  100. package/dist/src/relationships/index.js +1 -3
  101. package/dist/src/types.d.ts +6 -8
  102. package/dist/src/types.d.ts.map +1 -1
  103. package/dist/src/utils.d.ts +10 -32
  104. package/dist/src/utils.d.ts.map +1 -1
  105. package/dist/src/utils.js +13 -59
  106. package/package.json +5 -5
  107. package/dist/src/operations/utils/RelationshipTransactions.d.ts +0 -64
  108. package/dist/src/operations/utils/RelationshipTransactions.d.ts.map +0 -1
  109. package/dist/src/operations/utils/RelationshipTransactions.js +0 -125
  110. package/dist/src/relationships/BelongsToLink.d.ts +0 -51
  111. package/dist/src/relationships/BelongsToLink.d.ts.map +0 -1
  112. package/dist/src/relationships/BelongsToLink.js +0 -57
@@ -8,41 +8,201 @@ const utils_1 = require("../../utils");
8
8
  const utils_2 = require("../utils");
9
9
  const OperationBase_1 = __importDefault(require("../OperationBase"));
10
10
  const metadata_1 = __importDefault(require("../../metadata"));
11
+ const errors_1 = require("../../errors");
12
+ const utils_3 = require("../../metadata/utils");
11
13
  /**
12
- * Facilitates the operation of updating an existing entity in the database, including handling updates to its attributes and managing changes to its relationships. It will de-normalize data to support relationship links
14
+ * Represents an update operation for a DynaRecord-backed entity, handling both attribute updates and relationship consistency via denormalization.
13
15
  *
14
- * The `Update` operation supports updating entity attributes and ensures consistency in relationships, especially for "BelongsTo" relationships. It handles the complexity of managing foreign keys and associated "BelongsToLink" records, including creating new links for updated relationships and removing outdated links when necessary.
16
+ * This class supports updating an existing entity and managing its relational links, specifically:
17
+ * - Updating entity attributes while preserving schema constraints.
18
+ * - Managing "BelongsTo" relationship links by creating or removing associated denormalized records.
19
+ * - Ensuring that if a foreign key changes, the old link record is removed, preventing stale references.
15
20
  *
16
- * Only attributes defined on the model can be configured, and will be enforced via types and runtime schema validation.
21
+ * Only attributes defined on the model can be updated. Both compile-time and runtime checks help ensure that updated values are valid.
22
+ *
23
+ * **Example**
24
+ * ```typescript
25
+ * const updateOp = new Update(MyEntityClass);
26
+ * await updateOp.run("entityId", { name: "NewName", status: "active" });
27
+ * ```
17
28
  *
18
29
  * @template T - The type of the entity being updated, extending `DynaRecord`.
19
30
  */
20
31
  class Update extends OperationBase_1.default {
21
- #transactionBuilder;
22
- #entity;
23
- constructor(Entity) {
32
+ transactionBuilder;
33
+ constructor(Entity, transactionBuilder) {
24
34
  super(Entity);
25
- this.#transactionBuilder = new dynamo_utils_1.TransactWriteBuilder();
35
+ // Use the transaction builder passed to the class, or instantiate a new one
36
+ this.transactionBuilder = transactionBuilder ?? new dynamo_utils_1.TransactWriteBuilder();
26
37
  }
27
38
  /**
28
- * Update entity transactions, including transactions to create/update BelongsToLinks
29
- * @param id The id of the entity being updated
30
- * @param attributes Attributes on the model to update.
39
+ * Executes the update operation against DynamoDB.
40
+ *
41
+ * **What it does:**
42
+ * - Fetches the current state of the entity and any related "Has" relationship entities.
43
+ * - Constructs an update expression for the main entity's attributes.
44
+ * - Applies the same update expression to related entities and "BelongsTo" link records to maintain denormalized consistency.
45
+ * - Manages foreign key changes by creating new link items and removing old link items.
46
+ *
47
+ * @param id - The unique identifier of the entity being updated.
48
+ * @param attributes - Partial set of entity attributes to update. Must be defined on the entity's model.
49
+ * @returns A promise that resolves to the set of updated attributes as applied to the entity.
50
+ * @throws If the entity does not exist, an error is thrown.
31
51
  */
32
52
  async run(id, attributes) {
33
53
  const entityMeta = metadata_1.default.getEntity(this.EntityClass.name);
34
54
  const entityAttrs = entityMeta.parseRawEntityDefinedAttributesPartial(attributes);
35
- const updatedAttrs = this.buildUpdateItemTransaction(id, entityAttrs);
36
- await this.buildRelationshipTransactions(id, entityAttrs);
37
- await this.#transactionBuilder.executeTransaction();
55
+ const { updatedAttrs, expression } = this.buildUpdateMetadata(entityAttrs);
56
+ this.buildUpdateItemTransaction(id, expression);
57
+ // Only need to prefetch if the entity has relationships
58
+ if (entityMeta.allRelationships.length > 0) {
59
+ const belongsToRelMetaBeingUpdated = this.getBelongsToRelMetaAndKeyForUpdatedKeys(entityAttrs);
60
+ const entities = await this.preFetch(id, belongsToRelMetaBeingUpdated);
61
+ const preFetch = this.preProcessFetchedData(id, entities, belongsToRelMetaBeingUpdated);
62
+ const updatedEntity = {
63
+ ...preFetch.entityPreUpdate,
64
+ ...updatedAttrs,
65
+ id
66
+ };
67
+ this.buildUpdateRelatedEntityLinks(id, preFetch.relatedEntities, expression);
68
+ this.buildBelongsToTransactions(preFetch.entityPreUpdate, updatedEntity, expression, preFetch.newBelongsToEntityLookup);
69
+ }
70
+ await this.commitTransaction();
38
71
  return updatedAttrs;
39
72
  }
73
+ async commitTransaction() {
74
+ await this.transactionBuilder.executeTransaction();
75
+ }
40
76
  /**
41
- * Build the transaction to update the entity
42
- * @param id The id of the entity being updated
43
- * @param attributes Attributes on the model to update.
77
+ * BelongsToRelationship meta data and foreign key value pair for foreign keys being updated
78
+ * @param attributes
79
+ * @returns
44
80
  */
45
- buildUpdateItemTransaction(id, attributes) {
81
+ getBelongsToRelMetaAndKeyForUpdatedKeys(attributes) {
82
+ return this.entityMetadata.belongsToRelationships.reduce((acc, meta) => {
83
+ const foreignKeyVal = (0, utils_2.extractForeignKeyFromEntity)(meta, attributes);
84
+ if (foreignKeyVal !== undefined)
85
+ acc.push({ meta, foreignKeyVal });
86
+ return acc;
87
+ }, []);
88
+ }
89
+ /**
90
+ * Pre-fetches the target entity and any related entities from the database. This is done using a strong read operation to ensure consistency.
91
+ *
92
+ * **What it does:**
93
+ * - Retrieves the main entity and all linked entities in the entity's partition.
94
+ * - Performs a TransactGetItem operation on entities linked bai foreign keys (BelongsTo) to support denormalizing copies to associated partitions
95
+ * - Filters these entities to separate the main entity and its related link records.
96
+ *
97
+ * @param id - The unique identifier of the entity being fetched.
98
+ * @param belongsToRelFkAndMetas - BelongsTo relationship meta objects with foreign keys used for fetching associated records
99
+ * @returns A promise that resolves to an object containing:
100
+ * - `entityPreUpdate`: The current state of the entity before updates.
101
+ * - `relatedEntities`: An array of related link entities.
102
+ * @throws If the entity does not exist, it throws a NotFoundError.
103
+ * @private
104
+ */
105
+ async preFetch(id, belongsToRelFkAndMetas) {
106
+ const hasRelMetas = this.entityMetadata.hasRelationships.filter(rel => !(0, utils_3.isHasManyRelationship)(rel) || rel.uniDirectional !== true);
107
+ const { name: tableName } = this.tableMetadata;
108
+ const transactionBuilder = new dynamo_utils_1.TransactGetBuilder();
109
+ // Get the new BelongsTo relationship entities that are being updated
110
+ belongsToRelFkAndMetas.forEach(({ meta, foreignKeyVal }) => {
111
+ if (foreignKeyVal !== null) {
112
+ transactionBuilder.addGet({
113
+ TableName: tableName,
114
+ Key: {
115
+ [this.partitionKeyAlias]: meta.target.partitionKeyValue(foreignKeyVal),
116
+ [this.sortKeyAlias]: meta.target.name
117
+ }
118
+ });
119
+ }
120
+ });
121
+ const typeAlias = this.tableMetadata.defaultAttributes.type.alias;
122
+ // Get linked items from entity being updates partition as well as new foreign entities if adding or updating an FK
123
+ const [transactionResults, selfAndLinkedEntities] = await Promise.all([
124
+ transactionBuilder.executeTransaction(),
125
+ this.EntityClass.query(id, {
126
+ filter: {
127
+ type: [
128
+ this.EntityClass.name,
129
+ ...hasRelMetas.map(meta => meta.target.name)
130
+ ]
131
+ }
132
+ })
133
+ ]);
134
+ // Serialize table items to entities
135
+ const foreignEntities = transactionResults.reduce((acc, res) => {
136
+ if (res.Item !== undefined && (0, utils_1.isString)(res.Item[typeAlias])) {
137
+ const entityMeta = metadata_1.default.getEntity(res.Item[typeAlias]);
138
+ const entity = (0, utils_1.tableItemToEntity)(entityMeta.EntityClass, res.Item);
139
+ acc.push(entity);
140
+ }
141
+ return acc;
142
+ }, []);
143
+ return [...selfAndLinkedEntities, ...foreignEntities];
144
+ }
145
+ /**
146
+ * {reprocess pre-fetch data for processing
147
+ * @param id
148
+ * @param entities
149
+ * @param belongsToRelFkAndMetas
150
+ * @returns
151
+ */
152
+ preProcessFetchedData(id, entities, belongsToRelFkAndMetas) {
153
+ let entityPreUpdate;
154
+ const relatedEntities = [];
155
+ const newBelongsToEntityLookup = {};
156
+ entities.forEach(entity => {
157
+ if (id === entity.id) {
158
+ entityPreUpdate = entity;
159
+ }
160
+ else if (belongsToRelFkAndMetas.some(obj => obj.foreignKeyVal === entity.id)) {
161
+ newBelongsToEntityLookup[entity.id] = entity;
162
+ }
163
+ else {
164
+ relatedEntities.push(entity);
165
+ }
166
+ });
167
+ if (entityPreUpdate === undefined) {
168
+ throw new errors_1.NotFoundError(`${this.EntityClass.name} does not exist: ${id}`);
169
+ }
170
+ return { entityPreUpdate, relatedEntities, newBelongsToEntityLookup };
171
+ }
172
+ /**
173
+ * Constructs updated attributes and the corresponding DynamoDB update expression.
174
+ *
175
+ * **What it does:**
176
+ * - Merges the provided attributes with `updatedAt` (automatically set to the current time).
177
+ * - Converts the updated attributes into a DynamoDB update expression.
178
+ *
179
+ * @param attributes - The partial attributes to be updated on the entity.
180
+ * @returns An object containing:
181
+ * - `updatedAttrs`: The final updated attribute set.
182
+ * - `expression`: A DynamoDB update expression to apply these changes.
183
+ * @private
184
+ */
185
+ buildUpdateMetadata(attributes) {
186
+ const updatedAttrs = {
187
+ ...attributes,
188
+ updatedAt: new Date()
189
+ };
190
+ const tableAttrs = (0, utils_1.entityToTableItem)(this.EntityClass, updatedAttrs);
191
+ const expression = (0, utils_2.expressionBuilder)(tableAttrs);
192
+ return { updatedAttrs, expression };
193
+ }
194
+ /**
195
+ * Adds a transaction operation to update the main entity's record.
196
+ *
197
+ * **What it does:**
198
+ * - Builds a DynamoDB Update transaction using the provided update expression.
199
+ * - Adds a condition to ensure the entity exists before updating.
200
+ *
201
+ * @param id - The unique identifier of the entity being updated.
202
+ * @param updateExpression - The DynamoDB update expression describing the changes.
203
+ * @private
204
+ */
205
+ buildUpdateItemTransaction(id, updateExpression) {
46
206
  const { name: tableName } = this.tableMetadata;
47
207
  const pk = this.tableMetadata.partitionKeyAttribute.name;
48
208
  const sk = this.tableMetadata.sortKeyAttribute.name;
@@ -50,76 +210,253 @@ class Update extends OperationBase_1.default {
50
210
  [pk]: this.EntityClass.partitionKeyValue(id),
51
211
  [sk]: this.EntityClass.name
52
212
  };
53
- const updatedAttrs = {
54
- ...attributes,
55
- updatedAt: new Date()
56
- };
57
213
  const tableKeys = (0, utils_1.entityToTableItem)(this.EntityClass, keys);
58
- const tableAttrs = (0, utils_1.entityToTableItem)(this.EntityClass, updatedAttrs);
59
- const expression = (0, utils_2.expressionBuilder)(tableAttrs);
60
- this.#transactionBuilder.addUpdate({
214
+ this.transactionBuilder.addUpdate({
61
215
  TableName: tableName,
62
216
  Key: tableKeys,
63
- ConditionExpression: `attribute_exists(${this.partitionKeyAlias})`, // Only update the item if it exists
64
- ...expression
217
+ ConditionExpression: `attribute_exists(${this.partitionKeyAlias})`,
218
+ ...updateExpression
65
219
  }, `${this.EntityClass.name} with ID '${id}' does not exist`);
66
- return updatedAttrs;
67
220
  }
68
221
  /**
69
- * Builds the transactions to persist relationships
70
- * - Creates BelongsToLinks when a foreign key changes
71
- * - Removes outdated BelongsToLinks if the entity previously was associated with a different entity
72
- * @param id The id of the entity being updated
73
- * @param attributes Attributes on the model to update.
74
- */
75
- async buildRelationshipTransactions(id, attributes) {
76
- const entityData = { id, ...attributes };
77
- const relationshipTransactions = new utils_2.RelationshipTransactions({
78
- Entity: this.EntityClass,
79
- transactionBuilder: this.#transactionBuilder,
80
- belongsToHasManyCb: async (rel, entityId) => {
81
- const entity = await this.getEntity(entityId);
82
- this.buildDeleteOldBelongsToLinkTransaction(rel, "HasMany", entity);
83
- },
84
- belongsToHasOneCb: async (rel, entityId) => {
85
- const entity = await this.getEntity(entityId);
86
- this.buildDeleteOldBelongsToLinkTransaction(rel, "HasOne", entity);
222
+ * Builds all necessary transactions to handle "BelongsTo" or uni-directional "OwnedBy" relationships when updating the entity.
223
+ *
224
+ * **What it does:**
225
+ * - Checks if any foreign keys for "BelongsTo" or uni-directional "OwnedBy" relationships have changed.
226
+ * - If so, updates or creates new denormalized link records in the related partitions.
227
+ * - Removes old link records that are no longer valid due to foreign key changes.
228
+ *
229
+ * @param entityPreUpdate - The state of the entity before the update.
230
+ * @param updatedEntity - The entity state after proposed updates (partial attributes).
231
+ * @param updateExpression - The DynamoDB update expression representing the attribute updates.
232
+ * @private
233
+ */
234
+ buildBelongsToTransactions(entityPreUpdate, updatedEntity, updateExpression, newBelongsToEntityLookup) {
235
+ const entityId = entityPreUpdate.id;
236
+ const relMetas = this.entityMetadata.belongsToOrOwnedByRelationships;
237
+ for (const relMeta of relMetas) {
238
+ const foreignKey = (0, utils_2.extractForeignKeyFromEntity)(relMeta, updatedEntity);
239
+ const isUpdatingRelationshipId = foreignKey !== undefined;
240
+ if (isUpdatingRelationshipId && (0, utils_1.isNullableString)(foreignKey)) {
241
+ // Handle removal of old link if the foreign key changed.
242
+ const oldFk = (0, utils_2.extractForeignKeyFromEntity)(relMeta, entityPreUpdate);
243
+ const isAddingForeignKey = oldFk === undefined && foreignKey !== null;
244
+ const isUpdatingForeignKey = oldFk !== undefined && oldFk !== foreignKey;
245
+ const isRemovingForeignKey = isUpdatingForeignKey && foreignKey === null;
246
+ if (isAddingForeignKey) {
247
+ this.buildPutBelongsToLinkedRecords(updatedEntity, relMeta, foreignKey, newBelongsToEntityLookup, "attribute_not_exists", `${this.EntityClass.name} already has an associated ${relMeta.target.name}`);
248
+ }
249
+ else if (isRemovingForeignKey) {
250
+ this.removeForeignKeysTransactions(entityId, relMeta, oldFk);
251
+ }
252
+ else if (isUpdatingForeignKey) {
253
+ this.updateForeignKeyTransactions(updatedEntity, relMeta, foreignKey, oldFk, newBelongsToEntityLookup);
254
+ }
255
+ else if (foreignKey !== null) {
256
+ this.buildUpdateBelongsToLinkedRecords(entityId, relMeta, foreignKey, updateExpression);
257
+ }
87
258
  }
88
- });
89
- await relationshipTransactions.build(entityData);
259
+ }
90
260
  }
91
261
  /**
92
- * When updating the foreign key of an entity, delete the BelongsToLink in the previous relationships partition
93
- * @param rel
94
- * @param relType
95
- * @param entity
262
+ * Creates or updates the denormalized link record for a "BelongsTo" relationship to the related entity's partition.
263
+ *
264
+ * **What it does:**
265
+ * - Builds a DynamoDB Update transaction for the linked record identified by the relationship and foreign key.
266
+ * - Ensures the linked record exists before updating (if not, it will fail).
267
+ *
268
+ * @param entityId - The identifier of the main entity being updated.
269
+ * @param relMeta - Metadata describing the "BelongsTo" relationship.
270
+ * @param foreignKey - The new foreign key value after updates.
271
+ * @param updateExpression - The DynamoDB update expression for the attribute changes.
272
+ * @private
96
273
  */
97
- buildDeleteOldBelongsToLinkTransaction(rel, relType, entity) {
274
+ buildUpdateBelongsToLinkedRecords(entityId, relMeta, foreignKey, updateExpression) {
98
275
  const { name: tableName } = this.tableMetadata;
99
- const currentId = (0, utils_2.extractForeignKeyFromEntity)(rel, entity);
100
- if (entity !== undefined && currentId !== undefined) {
101
- const oldLinkKeys = {
102
- [this.partitionKeyAlias]: rel.target.partitionKeyValue(currentId),
103
- [this.sortKeyAlias]: relType === "HasMany"
104
- ? this.EntityClass.partitionKeyValue(entity.id)
105
- : this.EntityClass.name
106
- };
107
- this.#transactionBuilder.addDelete({
108
- TableName: tableName,
109
- Key: oldLinkKeys
110
- });
276
+ const newKey = (0, utils_2.buildBelongsToLinkKey)(this.EntityClass, entityId, relMeta, foreignKey);
277
+ this.transactionBuilder.addUpdate({
278
+ TableName: tableName,
279
+ Key: newKey,
280
+ ConditionExpression: `attribute_exists(${this.partitionKeyAlias})`,
281
+ ...updateExpression
282
+ });
283
+ }
284
+ /**
285
+ * Builds transactions to persist an updated/new belongs to record
286
+ * Denormalizes data to the entity being updates's partition and a link to the foreign entities partition
287
+ * Ensures that the new entity foreign key exists
288
+ * @param updatedEntity
289
+ * @param relMeta
290
+ * @param foreignKey
291
+ * @param newBelongsToEntityLookup
292
+ * @param persistToSelfCondition
293
+ * @param persistToSelfConditionErrMessage
294
+ */
295
+ buildPutBelongsToLinkedRecords(updatedEntity, relMeta, foreignKey, newBelongsToEntityLookup, persistToSelfCondition, persistToSelfConditionErrMessage) {
296
+ // Ensure that the new foreign key is valid and exists
297
+ this.buildEntityExistsCondition(relMeta, foreignKey);
298
+ // Denormalize entity being updated to foreign partition
299
+ this.buildLinkToForeignEntityTransaction(updatedEntity, relMeta, foreignKey);
300
+ if ((0, utils_3.isBelongsToRelationship)(relMeta)) {
301
+ // Add denormalized record for new entity to self
302
+ this.buildAddForeignEntityToSelfTransaction(updatedEntity, relMeta, foreignKey, newBelongsToEntityLookup, persistToSelfCondition, persistToSelfConditionErrMessage);
303
+ }
304
+ }
305
+ /**
306
+ * Builds a condition expression that an entity exists
307
+ * @param relMeta
308
+ * @param foreignKey
309
+ */
310
+ buildEntityExistsCondition(relMeta, foreignKey) {
311
+ const errMsg = `${relMeta.target.name} with ID '${foreignKey}' does not exist`;
312
+ const conditionCheck = {
313
+ TableName: this.tableMetadata.name,
314
+ Key: {
315
+ [this.partitionKeyAlias]: relMeta.target.partitionKeyValue(foreignKey),
316
+ [this.sortKeyAlias]: relMeta.target.name
317
+ },
318
+ ConditionExpression: `attribute_exists(${this.partitionKeyAlias})`
319
+ };
320
+ this.transactionBuilder.addConditionCheck(conditionCheck, errMsg);
321
+ }
322
+ /**
323
+ * Denormalizes a link for the entity being updated to the new entities partition
324
+ * @param updatedEntity
325
+ * @param relMeta
326
+ * @param foreignKey
327
+ */
328
+ buildLinkToForeignEntityTransaction(updatedEntity, relMeta, foreignKey) {
329
+ const key = (0, utils_2.buildBelongsToLinkKey)(this.EntityClass, updatedEntity.id, relMeta, foreignKey);
330
+ const tableItem = (0, utils_1.entityToTableItem)(this.EntityClass, updatedEntity);
331
+ this.transactionBuilder.addPut({
332
+ TableName: this.tableMetadata.name,
333
+ Item: { ...tableItem, ...key },
334
+ ConditionExpression: `attribute_not_exists(${this.partitionKeyAlias})`
335
+ }, `${relMeta.target.name} with id: ${foreignKey} already has an associated ${this.EntityClass.name}`);
336
+ }
337
+ /**
338
+ * Builds the transaction to add or replace the linked record for the foreign entity in the entity being updates partition
339
+ * @param updatedEntity
340
+ * @param relMeta
341
+ * @param foreignKey
342
+ * @param newBelongsToEntityLookup
343
+ * @param persistToSelfCondition
344
+ * @param persistToSelfConditionErrMessage
345
+ */
346
+ buildAddForeignEntityToSelfTransaction(updatedEntity, relMeta, foreignKey, newBelongsToEntityLookup, persistToSelfCondition, persistToSelfConditionErrMessage) {
347
+ const linkedEntity = newBelongsToEntityLookup[foreignKey];
348
+ if (linkedEntity === undefined) {
349
+ throw new errors_1.NotFoundError(`${relMeta.target.name} does not exist: ${foreignKey}`);
111
350
  }
351
+ const key = {
352
+ [this.partitionKeyAlias]: this.EntityClass.partitionKeyValue(updatedEntity.id),
353
+ [this.sortKeyAlias]: relMeta.target.name
354
+ };
355
+ const linkedRecordTableItem = (0, utils_1.entityToTableItem)(relMeta.target, linkedEntity);
356
+ this.transactionBuilder.addPut({
357
+ TableName: this.tableMetadata.name,
358
+ Item: { ...linkedRecordTableItem, ...key },
359
+ ConditionExpression: `${persistToSelfCondition}(${this.partitionKeyAlias})`
360
+ }, persistToSelfConditionErrMessage);
361
+ }
362
+ /**
363
+ * Removes the old link record associated with a previous "BelongsTo" foreign key when the foreign key changes.
364
+ *
365
+ * **What it does:**
366
+ * - Builds a DynamoDB Delete transaction to remove the stale link record from the old relationship partition.
367
+ *
368
+ * @param entityId - The identifier of the entity whose foreign key was changed.
369
+ * @param relMeta - Metadata describing the "BelongsTo" relationship.
370
+ * @param oldForeignKey - The old foreign key value that should no longer link to the entity.
371
+ * @private
372
+ */
373
+ removeForeignKeysTransactions(entityId, relMeta, oldForeignKey) {
374
+ // Keys to delete the denormalized record from entities own partition
375
+ const oldKeysToSelf = {
376
+ [this.partitionKeyAlias]: this.EntityClass.partitionKeyValue(entityId),
377
+ [this.sortKeyAlias]: relMeta.target.name
378
+ };
379
+ // Keys to delete the linked record from the foreign entities partition
380
+ const oldKeysToForeignEntity = (0, utils_2.buildBelongsToLinkKey)(this.EntityClass, entityId, relMeta, oldForeignKey);
381
+ // OwnedByRelationships do not have a record to delete from their own partition because its unidirectional
382
+ if ((0, utils_3.isBelongsToRelationship)(relMeta)) {
383
+ this.transactionBuilder.addDelete({
384
+ TableName: this.tableMetadata.name,
385
+ Key: oldKeysToSelf
386
+ }, `Failed to delete denormalized record with keys: ${JSON.stringify(oldKeysToSelf)}`);
387
+ }
388
+ this.transactionBuilder.addDelete({
389
+ TableName: this.tableMetadata.name,
390
+ Key: oldKeysToForeignEntity
391
+ }, `Failed to delete denormalized record with keys: ${JSON.stringify(oldKeysToForeignEntity)}`);
392
+ }
393
+ /**
394
+ * Builds the transactions for updating the foreign key of an entity from one key to another
395
+ * @param updatedEntity
396
+ * @param relMeta
397
+ * @param newForeignKey
398
+ * @param oldForeignKey
399
+ * @param newBelongsToEntityLookup
400
+ */
401
+ updateForeignKeyTransactions(updatedEntity, relMeta, newForeignKey, oldForeignKey, newBelongsToEntityLookup) {
402
+ // Keys to delete the linked record from the foreign entities partition
403
+ const oldKeysToForeignEntity = (0, utils_2.buildBelongsToLinkKey)(this.EntityClass, updatedEntity.id, relMeta, oldForeignKey);
404
+ this.transactionBuilder.addDelete({
405
+ TableName: this.tableMetadata.name,
406
+ Key: oldKeysToForeignEntity
407
+ });
408
+ this.buildPutBelongsToLinkedRecords(updatedEntity, relMeta, newForeignKey, newBelongsToEntityLookup, "attribute_exists");
112
409
  }
113
410
  /**
114
- * If updating a ForeignKey, look up the current state of the item to build transactions
411
+ * Builds transactions to update all related entities that exist in the main entity's partition, applying the same attribute updates.
412
+ *
413
+ * **What it does:**
414
+ * - For each linked entity found in `preFetch`, attempts to apply the same update expression.
415
+ * - Ensures each related entity exists before attempting to update.
416
+ *
417
+ * @param relatedEntities - An array of entities that are related to the primary entity and need synchronized attribute updates.
418
+ * @param expression - The DynamoDB update expression to apply to related entities.
419
+ * @private
115
420
  */
116
- async getEntity(id) {
117
- // Only get the item once per transaction
118
- if (this.#entity !== undefined)
119
- return this.#entity;
120
- const res = (await this.EntityClass.findById(id));
121
- this.#entity = res ?? undefined;
122
- return this.#entity;
421
+ buildUpdateRelatedEntityLinks(entityId, relatedEntities, expression) {
422
+ const hasAndBelongsToManyLookup = this.buildHasAndBelongsToManyRelLookup();
423
+ relatedEntities.forEach(entity => {
424
+ this.transactionBuilder.addUpdate({
425
+ TableName: this.tableMetadata.name,
426
+ Key: this.buildUpdatedRelatedEntityLinkKey(entityId, entity, hasAndBelongsToManyLookup),
427
+ ConditionExpression: `attribute_exists(${this.partitionKeyAlias})`,
428
+ ...expression
429
+ }, `${entity.constructor.name} (${entity.id}) is not associated with ${this.EntityClass.name} (${entityId})`);
430
+ });
431
+ }
432
+ /**
433
+ * Builds the table key for a related entity that needs to be updated
434
+ * @param id - The id of the entity being updated
435
+ * @param entity
436
+ * @param relLookup
437
+ * @returns
438
+ */
439
+ buildUpdatedRelatedEntityLinkKey(id, entity, relLookup) {
440
+ const isHasAndBelongsToManyRel = relLookup[entity.type] !== undefined;
441
+ const sortKey = isHasAndBelongsToManyRel
442
+ ? this.EntityClass.partitionKeyValue(id)
443
+ : this.EntityClass.name;
444
+ return {
445
+ [this.partitionKeyAlias]: entity.partitionKeyValue(),
446
+ [this.sortKeyAlias]: sortKey
447
+ };
448
+ }
449
+ /**
450
+ * Build a lookup object to lookup a HasAndBelongsToMany relationship for an entity. Used for processing to avoid excessive looping
451
+ * @returns
452
+ */
453
+ buildHasAndBelongsToManyRelLookup() {
454
+ return Object.values(this.entityMetadata.relationships).reduce((acc, rel) => {
455
+ if (rel.type === "HasAndBelongsToMany") {
456
+ acc[rel.target.name] = rel;
457
+ }
458
+ return acc;
459
+ }, {});
123
460
  }
124
461
  }
125
462
  exports.default = Update;
@@ -0,0 +1,10 @@
1
+ import type DynaRecord from "../../DynaRecord";
2
+ import Update from "./Update";
3
+ /**
4
+ * Runs Update operation with out committing the transaction
5
+ */
6
+ declare class UpdateDryRun<T extends DynaRecord> extends Update<T> {
7
+ protected commitTransaction(): Promise<void>;
8
+ }
9
+ export default UpdateDryRun;
10
+ //# sourceMappingURL=UpdateDryRun.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UpdateDryRun.d.ts","sourceRoot":"","sources":["../../../../src/operations/Update/UpdateDryRun.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,MAAM,MAAM,UAAU,CAAC;AAE9B;;GAEG;AACH,cAAM,YAAY,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,MAAM,CAAC,CAAC,CAAC;cAC/B,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;CAG5D;AAED,eAAe,YAAY,CAAC"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const Update_1 = __importDefault(require("./Update"));
7
+ /**
8
+ * Runs Update operation with out committing the transaction
9
+ */
10
+ class UpdateDryRun extends Update_1.default {
11
+ async commitTransaction() {
12
+ // No-op
13
+ }
14
+ }
15
+ exports.default = UpdateDryRun;
@@ -1,3 +1,4 @@
1
1
  export { default as Update } from "./Update";
2
2
  export * from "./types";
3
+ export { default as UpdateDryRun } from "./UpdateDryRun";
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/operations/Update/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/operations/Update/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
@@ -17,7 +17,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.Update = void 0;
20
+ exports.UpdateDryRun = exports.Update = void 0;
21
21
  var Update_1 = require("./Update");
22
22
  Object.defineProperty(exports, "Update", { enumerable: true, get: function () { return __importDefault(Update_1).default; } });
23
23
  __exportStar(require("./types"), exports);
24
+ var UpdateDryRun_1 = require("./UpdateDryRun");
25
+ Object.defineProperty(exports, "UpdateDryRun", { enumerable: true, get: function () { return __importDefault(UpdateDryRun_1).default; } });
@@ -44,10 +44,15 @@ export type ForeignKeyToValue<T> = {
44
44
  export type RelationshipAttributeNames<T> = {
45
45
  [K in keyof T]: Exclude<T[K], undefined> extends DynaRecord | DynaRecord[] ? K : never;
46
46
  }[keyof T];
47
+ /**
48
+ * Entity class instance with attributes excluding relationship attributes
49
+ */
50
+ export type EntityAttributesInstance<T extends DynaRecord> = Omit<T, RelationshipAttributeNames<T>>;
47
51
  /**
48
52
  * Entity attributes excluding relationship attributes
53
+ * Represents the raw attributes of a class (no functions)
49
54
  */
50
- export type EntityAttributes<T extends DynaRecord> = Omit<T, RelationshipAttributeNames<T> | FunctionFields<T>>;
55
+ export type EntityAttributesOnly<T extends DynaRecord> = Omit<T, RelationshipAttributeNames<T> | FunctionFields<T>>;
51
56
  /**
52
57
  * Entity attributes for default fields
53
58
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/operations/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,QAAQ,EACR,YAAY,EACZ,OAAO,EACR,MAAM,UAAU,CAAC;AAElB;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI;KACpC,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,YAAY,GAAG,CAAC,GAAG,KAAK;CACtD,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;KAC/B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,OAAO,GAAG,CAAC,GAAG,KAAK;CACjD,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;;;GAKG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;KAC7B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAAG,CAAC,GAAG,KAAK;CACjE,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;KAChC,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,kBAAkB,GAC3C,QAAQ,CAAC,MAAM,CAAC,GAChB,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,GACrB,MAAM,GACN,CAAC,CAAC,CAAC,CAAC;CACX,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;KACzC,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,SAAS,UAAU,GAAG,UAAU,EAAE,GACtE,CAAC,GACD,KAAK;CACV,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,UAAU,IAAI,IAAI,CACvD,CAAC,EACD,0BAA0B,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAClD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,IAAI,CAC7C,UAAU,EACV,OAAO,CAAC,MAAM,UAAU,EAAE,aAAa,CAAC,CACzC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,UAAU,IAAI,IAAI,CAC9D,iBAAiB,CAAC,CAAC,CAAC,EAClB,MAAM,UAAU,GAChB,0BAA0B,CAAC,CAAC,CAAC,GAC7B,cAAc,CAAC,CAAC,CAAC,GACjB,qBAAqB,CAAC,CAAC,CAAC,GACxB,gBAAgB,CAAC,CAAC,CAAC,CACtB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/operations/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,QAAQ,EACR,YAAY,EACZ,OAAO,EACR,MAAM,UAAU,CAAC;AAElB;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI;KACpC,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,YAAY,GAAG,CAAC,GAAG,KAAK;CACtD,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;KAC/B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,OAAO,GAAG,CAAC,GAAG,KAAK;CACjD,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;;;GAKG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;KAC7B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAAG,CAAC,GAAG,KAAK;CACjE,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;KAChC,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,kBAAkB,GAC3C,QAAQ,CAAC,MAAM,CAAC,GAChB,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,GACrB,MAAM,GACN,CAAC,CAAC,CAAC,CAAC;CACX,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;KACzC,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,SAAS,UAAU,GAAG,UAAU,EAAE,GACtE,CAAC,GACD,KAAK;CACV,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,wBAAwB,CAAC,CAAC,SAAS,UAAU,IAAI,IAAI,CAC/D,CAAC,EACD,0BAA0B,CAAC,CAAC,CAAC,CAC9B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,UAAU,IAAI,IAAI,CAC3D,CAAC,EACD,0BAA0B,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAClD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,IAAI,CAC7C,UAAU,EACV,OAAO,CAAC,MAAM,UAAU,EAAE,aAAa,CAAC,CACzC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,uBAAuB,CAAC,CAAC,SAAS,UAAU,IAAI,IAAI,CAC9D,iBAAiB,CAAC,CAAC,CAAC,EAClB,MAAM,UAAU,GAChB,0BAA0B,CAAC,CAAC,CAAC,GAC7B,cAAc,CAAC,CAAC,CAAC,GACjB,qBAAqB,CAAC,CAAC,CAAC,GACxB,gBAAgB,CAAC,CAAC,CAAC,CACtB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"expressionBuilder.d.ts","sourceRoot":"","sources":["../../../../src/operations/utils/expressionBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EACV,gBAAgB,EAGjB,MAAM,SAAS,CAAC;AAUjB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,eAChB,eAAe,KAC1B,gBAyBF,CAAC"}
1
+ {"version":3,"file":"expressionBuilder.d.ts","sourceRoot":"","sources":["../../../../src/operations/utils/expressionBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EACV,gBAAgB,EAGjB,MAAM,SAAS,CAAC;AAUjB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,eAChB,eAAe,KAC1B,gBAoBF,CAAC"}
@@ -10,12 +10,9 @@ const expressionBuilder = (tableAttrs) => {
10
10
  const sorted = sortAttributesByOperand(tableAttrs);
11
11
  const setExpression = buildUpdateSetExpression(sorted.set);
12
12
  const removeExpression = buildUpdateRemoveExpression(sorted.remove);
13
- const hasSetOperation = Object.keys(setExpression.ExpressionAttributeValues).length > 0;
14
13
  return {
15
14
  // If the operation has only REMOVE actions, it will not have expression attribute values
16
- ExpressionAttributeValues: hasSetOperation
17
- ? setExpression.ExpressionAttributeValues
18
- : undefined,
15
+ ExpressionAttributeValues: setExpression.ExpressionAttributeValues,
19
16
  ExpressionAttributeNames: {
20
17
  ...setExpression.ExpressionAttributeNames,
21
18
  ...removeExpression.ExpressionAttributeNames
@@ -1,4 +1,3 @@
1
- export { default as RelationshipTransactions } from "./RelationshipTransactions";
2
1
  export * from "./expressionBuilder";
3
2
  export * from "./types";
4
3
  export * from "./utils";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/operations/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACjF,cAAc,qBAAqB,CAAC;AACpC,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/operations/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"}