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