dyna-record 0.4.0 → 0.4.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.
package/README.md CHANGED
@@ -357,6 +357,25 @@ const grade: Grade = await Grade.create({
357
357
  });
358
358
  ```
359
359
 
360
+ #### Skipping Referential Integrity Checks
361
+
362
+ By default, when creating entities with foreign key references, dyna-record performs condition checks to ensure that referenced entities exist. This prevents creating entities with invalid foreign key references. However, in high-contention or high-throughput systems where the same foreign key may be referenced in parallel operations, these condition checks can fail due to transaction conflicts. In scenarios such as bulk imports or when you've already verified the references, you may want to skip these checks to prevent such failures.
363
+
364
+ To skip referential integrity checks, pass an options object as the second parameter with `referentialIntegrityCheck: false`:
365
+
366
+ ```typescript
367
+ const grade: Grade = await Grade.create(
368
+ {
369
+ gradeValue: "A+",
370
+ assignmentId: "123",
371
+ studentId: "456"
372
+ },
373
+ { referentialIntegrityCheck: false }
374
+ );
375
+ ```
376
+
377
+ **Note:** When `referentialIntegrityCheck` is set to `false`, the condition checks that verify foreign key references exist are skipped. This means you can create entities even if the referenced entities don't exist, which may lead to data integrity issues. Use this option with caution.
378
+
360
379
  #### Error handling
361
380
 
362
381
  The method is designed to throw errors under various conditions, such as transaction cancellation due to failed conditional checks. For instance, if you attempt to create a `Grade` for an `Assignment` that already has one, the method throws a [TransactionWriteFailedError](https://dyna-record.com/classes/TransactionWriteFailedError.html).
@@ -549,6 +568,31 @@ const updatedInstance = await petInstance.update({
549
568
  });
550
569
  ```
551
570
 
571
+ #### Skipping Referential Integrity Checks
572
+
573
+ By default, when updating entities with foreign key references, dyna-record performs condition checks to ensure that referenced entities exist. This prevents updating entities with invalid foreign key references. However, in high-contention or high-throughput systems where the same foreign key may be referenced in parallel operations, these condition checks can fail due to transaction conflicts. In scenarios such as bulk updates or when you've already verified the references, you may want to skip these checks to prevent such failures.
574
+
575
+ To skip referential integrity checks, pass an options object as the third parameter with `referentialIntegrityCheck: false`:
576
+
577
+ ```typescript
578
+ await PaymentMethod.update(
579
+ "123",
580
+ { customerId: "456" },
581
+ { referentialIntegrityCheck: false }
582
+ );
583
+ ```
584
+
585
+ For instance methods:
586
+
587
+ ```typescript
588
+ const updatedInstance = await paymentMethodInstance.update(
589
+ { customerId: "456" },
590
+ { referentialIntegrityCheck: false }
591
+ );
592
+ ```
593
+
594
+ **Note:** When `referentialIntegrityCheck` is set to `false`, the condition checks that verify foreign key references exist are skipped. This means you can update entities even if the referenced entities don't exist, which may lead to data integrity issues. Use this option with caution.
595
+
552
596
  ### Delete
553
597
 
554
598
  [Docs](https://dyna-record.com/classes/default.html#delete)
@@ -181,24 +181,36 @@ declare abstract class DynaRecord implements DynaRecordBase {
181
181
  */
182
182
  static query<T extends DynaRecord>(this: EntityClass<T>, key: IndexKeyConditions<T>, options: OptionsWithIndex): Promise<QueryResults<T>>;
183
183
  /**
184
- * Create an entity. If foreign keys are included in the attributes then links will be demoralized accordingly
184
+ * Create an entity. If foreign keys are included in the attributes then links will be denormalized accordingly
185
185
  * @param attributes - Attributes of the model to create
186
+ * @param options - Optional operation options including referentialIntegrityCheck flag
186
187
  * @returns The new Entity
187
188
  *
189
+ * @example Basic usage
188
190
  * ```typescript
189
191
  * const newUser = await User.create({ name: "Alice", email: "alice@example.com", profileId: "123" });
190
192
  * ```
193
+ *
194
+ * @example With referential integrity check disabled
195
+ * ```typescript
196
+ * const newUser = await User.create(
197
+ * { name: "Alice", email: "alice@example.com", profileId: "123" },
198
+ * { referentialIntegrityCheck: false }
199
+ * );
200
+ * ```
191
201
  */
192
- static create<T extends DynaRecord>(this: EntityClass<T>, attributes: CreateOptions<T>): Promise<ReturnType<Create<T>["run"]>>;
202
+ static create<T extends DynaRecord>(this: EntityClass<T>, attributes: CreateOptions<T>, options?: {
203
+ referentialIntegrityCheck?: boolean;
204
+ }): Promise<ReturnType<Create<T>["run"]>>;
193
205
  /**
194
- * Update an entity. If foreign keys are included in the attribute then:
206
+ * Update an entity. If foreign keys are included in the attributes then:
195
207
  * - Manages associated relationship links as needed
196
208
  * - If the entity already had a foreign key relationship, then denormalized records will be deleted from each partition
197
209
  * - If the foreign key is not nullable then a {@link NullConstraintViolationError} is thrown.
198
210
  * - Validation errors will be thrown if the attribute being removed is not nullable
199
211
  * @param id - The id of the entity to update
200
212
  * @param attributes - Attributes to update
201
- *
213
+ * @param options - Optional operation options including referentialIntegrityCheck flag
202
214
  *
203
215
  * @example Updating an entity.
204
216
  * ```typescript
@@ -209,12 +221,22 @@ declare abstract class DynaRecord implements DynaRecordBase {
209
221
  * ```typescript
210
222
  * await User.update("userId", { email: "newemail@example.com", someKey: null });
211
223
  * ```
224
+ *
225
+ * @example With referential integrity check disabled
226
+ * ```typescript
227
+ * await User.update(
228
+ * "userId",
229
+ * { email: "newemail@example.com", profileId: 789 },
230
+ * { referentialIntegrityCheck: false }
231
+ * );
232
+ * ```
212
233
  */
213
- static update<T extends DynaRecord>(this: EntityClass<T>, id: string, attributes: UpdateOptions<T>): Promise<void>;
234
+ static update<T extends DynaRecord>(this: EntityClass<T>, id: string, attributes: UpdateOptions<T>, options?: {
235
+ referentialIntegrityCheck?: boolean;
236
+ }): Promise<void>;
214
237
  /**
215
238
  * Same as the static `update` method but on an instance. Returns the full updated instance
216
239
  *
217
- *
218
240
  * @example Updating an entity.
219
241
  * ```typescript
220
242
  * const updatedInstance = await instance.update({ email: "newemail@example.com", profileId: 789 });
@@ -224,8 +246,18 @@ declare abstract class DynaRecord implements DynaRecordBase {
224
246
  * ```typescript
225
247
  * const updatedInstance = await instance.update({ email: "newemail@example.com", someKey: null });
226
248
  * ```
249
+ *
250
+ * @example With referential integrity check disabled
251
+ * ```typescript
252
+ * const updatedInstance = await instance.update(
253
+ * { email: "newemail@example.com", profileId: 789 },
254
+ * { referentialIntegrityCheck: false }
255
+ * );
256
+ * ```
227
257
  */
228
- update<T extends this>(attributes: UpdateOptions<T>): Promise<EntityAttributesInstance<T>>;
258
+ update<T extends this>(attributes: UpdateOptions<T>, options?: {
259
+ referentialIntegrityCheck?: boolean;
260
+ }): Promise<EntityAttributesInstance<T>>;
229
261
  /**
230
262
  * Delete an entity by ID
231
263
  * - Delete all denormalized records
@@ -1 +1 @@
1
- {"version":3,"file":"DynaRecord.d.ts","sourceRoot":"","sources":["../../src/DynaRecord.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DynaRecord.d.ts","sourceRoot":"","sources":["../../src/DynaRecord.ts"],"names":[],"mappings":"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"}
@@ -145,27 +145,37 @@ let DynaRecord = (() => {
145
145
  return await op.run(key, options);
146
146
  }
147
147
  /**
148
- * Create an entity. If foreign keys are included in the attributes then links will be demoralized accordingly
148
+ * Create an entity. If foreign keys are included in the attributes then links will be denormalized accordingly
149
149
  * @param attributes - Attributes of the model to create
150
+ * @param options - Optional operation options including referentialIntegrityCheck flag
150
151
  * @returns The new Entity
151
152
  *
153
+ * @example Basic usage
152
154
  * ```typescript
153
155
  * const newUser = await User.create({ name: "Alice", email: "alice@example.com", profileId: "123" });
154
156
  * ```
157
+ *
158
+ * @example With referential integrity check disabled
159
+ * ```typescript
160
+ * const newUser = await User.create(
161
+ * { name: "Alice", email: "alice@example.com", profileId: "123" },
162
+ * { referentialIntegrityCheck: false }
163
+ * );
164
+ * ```
155
165
  */
156
- static async create(attributes) {
166
+ static async create(attributes, options) {
157
167
  const op = new operations_1.Create(this);
158
- return await op.run(attributes);
168
+ return await op.run(attributes, options);
159
169
  }
160
170
  /**
161
- * Update an entity. If foreign keys are included in the attribute then:
171
+ * Update an entity. If foreign keys are included in the attributes then:
162
172
  * - Manages associated relationship links as needed
163
173
  * - If the entity already had a foreign key relationship, then denormalized records will be deleted from each partition
164
174
  * - If the foreign key is not nullable then a {@link NullConstraintViolationError} is thrown.
165
175
  * - Validation errors will be thrown if the attribute being removed is not nullable
166
176
  * @param id - The id of the entity to update
167
177
  * @param attributes - Attributes to update
168
- *
178
+ * @param options - Optional operation options including referentialIntegrityCheck flag
169
179
  *
170
180
  * @example Updating an entity.
171
181
  * ```typescript
@@ -176,15 +186,23 @@ let DynaRecord = (() => {
176
186
  * ```typescript
177
187
  * await User.update("userId", { email: "newemail@example.com", someKey: null });
178
188
  * ```
189
+ *
190
+ * @example With referential integrity check disabled
191
+ * ```typescript
192
+ * await User.update(
193
+ * "userId",
194
+ * { email: "newemail@example.com", profileId: 789 },
195
+ * { referentialIntegrityCheck: false }
196
+ * );
197
+ * ```
179
198
  */
180
- static async update(id, attributes) {
199
+ static async update(id, attributes, options) {
181
200
  const op = new operations_1.Update(this);
182
- await op.run(id, attributes);
201
+ await op.run(id, attributes, options);
183
202
  }
184
203
  /**
185
204
  * Same as the static `update` method but on an instance. Returns the full updated instance
186
205
  *
187
- *
188
206
  * @example Updating an entity.
189
207
  * ```typescript
190
208
  * const updatedInstance = await instance.update({ email: "newemail@example.com", profileId: 789 });
@@ -194,11 +212,19 @@ let DynaRecord = (() => {
194
212
  * ```typescript
195
213
  * const updatedInstance = await instance.update({ email: "newemail@example.com", someKey: null });
196
214
  * ```
215
+ *
216
+ * @example With referential integrity check disabled
217
+ * ```typescript
218
+ * const updatedInstance = await instance.update(
219
+ * { email: "newemail@example.com", profileId: 789 },
220
+ * { referentialIntegrityCheck: false }
221
+ * );
222
+ * ```
197
223
  */
198
- async update(attributes) {
224
+ async update(attributes, options) {
199
225
  const InstanceClass = this.constructor;
200
226
  const op = new operations_1.Update(InstanceClass);
201
- const updatedAttributes = await op.run(this.id, attributes);
227
+ const updatedAttributes = await op.run(this.id, attributes, options);
202
228
  const clone = structuredClone(this);
203
229
  // Update the current instance with new attributes
204
230
  Object.assign(clone, updatedAttributes);
@@ -1,7 +1,7 @@
1
1
  import type DynaRecord from "../../DynaRecord";
2
2
  import type { EntityClass } from "../../types";
3
3
  import OperationBase from "../OperationBase";
4
- import type { CreateOptions } from "./types";
4
+ import type { CreateOptions, CreateOperationOptions } from "./types";
5
5
  import { type EntityAttributesOnly } from "../types";
6
6
  /**
7
7
  * Represents an operation to create a new entity record in DynamoDB, including all necessary
@@ -37,11 +37,12 @@ declare class Create<T extends DynaRecord> extends OperationBase<T> {
37
37
  * partition (due to "BelongsTo" links), retrieves and inserts those link records.
38
38
  *
39
39
  * @param attributes - Attributes to initialize the new entity. Must be defined on the model and valid per schema constraints.
40
+ * @param options - Optional operation options including referentialIntegrityCheck flag.
40
41
  * @returns A promise that resolves to the newly created entity with all attributes, including automatically set fields.
41
42
  * @throws If the entity already exists, a uniqueness violation error is raised.
42
- * @throws If a required foreign key does not correspond to an existing entity, an error is raised.
43
+ * @throws If a required foreign key does not correspond to an existing entity, an error is raised (unless referentialIntegrityCheck is false).
43
44
  */
44
- run(attributes: CreateOptions<T>): Promise<EntityAttributesOnly<T>>;
45
+ run(attributes: CreateOptions<T>, options?: CreateOperationOptions): Promise<EntityAttributesOnly<T>>;
45
46
  /**
46
47
  * Builds and returns entity attributes that must be reserved for system usage.
47
48
  *
@@ -75,6 +76,7 @@ declare class Create<T extends DynaRecord> extends OperationBase<T> {
75
76
  *
76
77
  * @param entityData - The complete set of entity attributes for the new entity.
77
78
  * @param tableItem - The main entity's DynamoDB table item.
79
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
78
80
  * @private
79
81
  */
80
82
  private buildBelongsToTransactions;
@@ -83,6 +85,8 @@ declare class Create<T extends DynaRecord> extends OperationBase<T> {
83
85
  * Ensures referenced entities exist even when denormalised access patterns are not defined.
84
86
  *
85
87
  * @param entityData - Attributes being persisted for the new entity.
88
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
89
+ * @private
86
90
  */
87
91
  private buildStandaloneForeignKeyConditionChecks;
88
92
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Create.d.ts","sourceRoot":"","sources":["../../../../src/operations/Create/Create.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAmB,WAAW,EAAE,MAAM,aAAa,CAAC;AAOhE,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,UAAU,CAAC;AAIlB;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAM,MAAM,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;;gBAG7C,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAKlC;;;;;;;;;;;;;;;;OAgBG;IACU,GAAG,CACd,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;IA6BnC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,uBAAuB;IA8B/B;;;;;;;;;OASG;IACH,OAAO,CAAC,uBAAuB;IAiB/B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IAkClC;;;;;OAKG;IACH,OAAO,CAAC,wCAAwC;IA8BhD;;;;;;;;;;;OAWG;YACW,sBAAsB;IAmCpC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,2CAA2C;IAmBnD;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,uCAAuC;CA0BhD;AAED,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Create.d.ts","sourceRoot":"","sources":["../../../../src/operations/Create/Create.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAmB,WAAW,EAAE,MAAM,aAAa,CAAC;AAOhE,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AACrE,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,UAAU,CAAC;AAIlB;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAM,MAAM,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;;gBAG7C,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAKlC;;;;;;;;;;;;;;;;;OAiBG;IACU,GAAG,CACd,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,EAC5B,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;IAuCnC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,uBAAuB;IA8B/B;;;;;;;;;OASG;IACH,OAAO,CAAC,uBAAuB;IAiB/B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,0BAA0B;IAqClC;;;;;;;OAOG;IACH,OAAO,CAAC,wCAAwC;IAkChD;;;;;;;;;;;OAWG;YACW,sBAAsB;IAmCpC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,2CAA2C;IAmBnD;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,uCAAuC;CA0BhD;AAED,eAAe,MAAM,CAAC"}
@@ -46,18 +46,20 @@ class Create extends OperationBase_1.default {
46
46
  * partition (due to "BelongsTo" links), retrieves and inserts those link records.
47
47
  *
48
48
  * @param attributes - Attributes to initialize the new entity. Must be defined on the model and valid per schema constraints.
49
+ * @param options - Optional operation options including referentialIntegrityCheck flag.
49
50
  * @returns A promise that resolves to the newly created entity with all attributes, including automatically set fields.
50
51
  * @throws If the entity already exists, a uniqueness violation error is raised.
51
- * @throws If a required foreign key does not correspond to an existing entity, an error is raised.
52
+ * @throws If a required foreign key does not correspond to an existing entity, an error is raised (unless referentialIntegrityCheck is false).
52
53
  */
53
- async run(attributes) {
54
+ async run(attributes, options) {
55
+ const referentialIntegrityCheck = options?.referentialIntegrityCheck ?? true;
54
56
  const entityAttrs = this.entityMetadata.parseRawEntityDefinedAttributes(attributes);
55
57
  const reservedAttrs = this.buildReservedAttributes(entityAttrs);
56
58
  const entityData = { ...reservedAttrs, ...entityAttrs };
57
59
  const tableItem = (0, utils_1.entityToTableItem)(this.EntityClass, entityData);
58
60
  this.buildPutItemTransaction(tableItem, entityData.id);
59
- this.buildBelongsToTransactions(entityData, tableItem);
60
- this.buildStandaloneForeignKeyConditionChecks(entityData);
61
+ this.buildBelongsToTransactions(entityData, tableItem, referentialIntegrityCheck);
62
+ this.buildStandaloneForeignKeyConditionChecks(entityData, referentialIntegrityCheck);
61
63
  // Attempt to fetch all belongs-to entities to properly create reverse denormalization links
62
64
  const belongsToTableItems = await this.getBelongsToTableItems(entityData);
63
65
  // If there are any belongs-to relationships, add the inverse link records into the new entity's partition
@@ -127,16 +129,19 @@ class Create extends OperationBase_1.default {
127
129
  *
128
130
  * @param entityData - The complete set of entity attributes for the new entity.
129
131
  * @param tableItem - The main entity's DynamoDB table item.
132
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
130
133
  * @private
131
134
  */
132
- buildBelongsToTransactions(entityData, tableItem) {
135
+ buildBelongsToTransactions(entityData, tableItem, referentialIntegrityCheck) {
133
136
  const tableName = this.tableMetadata.name;
134
137
  const relMetadata = this.entityMetadata.belongsToOrOwnedByRelationships;
135
138
  for (const relMeta of relMetadata) {
136
139
  const foreignKey = (0, utils_2.extractForeignKeyFromEntity)(relMeta, entityData);
137
140
  if (foreignKey !== undefined) {
138
141
  // Ensure referenced entity exists before linking
139
- this.buildRelationshipExistsConditionTransaction(relMeta, foreignKey);
142
+ if (referentialIntegrityCheck) {
143
+ this.buildRelationshipExistsConditionTransaction(relMeta, foreignKey);
144
+ }
140
145
  const key = (0, utils_2.buildBelongsToLinkKey)(this.EntityClass, entityData.id, relMeta, foreignKey);
141
146
  this.#transactionBuilder.addPut({
142
147
  TableName: tableName,
@@ -151,8 +156,13 @@ class Create extends OperationBase_1.default {
151
156
  * Ensures referenced entities exist even when denormalised access patterns are not defined.
152
157
  *
153
158
  * @param entityData - Attributes being persisted for the new entity.
159
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
160
+ * @private
154
161
  */
155
- buildStandaloneForeignKeyConditionChecks(entityData) {
162
+ buildStandaloneForeignKeyConditionChecks(entityData, referentialIntegrityCheck) {
163
+ if (!referentialIntegrityCheck) {
164
+ return;
165
+ }
156
166
  const standaloneForeignKeys = this.entityMetadata.standaloneForeignKeyAttributes;
157
167
  for (const attrMeta of standaloneForeignKeys) {
158
168
  const target = attrMeta.foreignKeyTarget;
@@ -1,5 +1,17 @@
1
1
  import type DynaRecord from "../../DynaRecord";
2
2
  import type { EntityDefinedAttributes } from "../types";
3
+ /**
4
+ * Options for create operations
5
+ */
6
+ export interface CreateOperationOptions {
7
+ /**
8
+ * Whether to perform referential integrity checks for foreign key references.
9
+ * When `true` (default), condition checks are added to verify that referenced entities exist.
10
+ * When `false`, these condition checks are skipped, allowing creation even if foreign key references don't exist.
11
+ * @default true
12
+ */
13
+ referentialIntegrityCheck?: boolean;
14
+ }
3
15
  /**
4
16
  * Entity attribute fields that can be set on create. Excludes that are managed by dyna-record
5
17
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/operations/Create/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD;;GAEG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,UAAU,IAAI,uBAAuB,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/operations/Create/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,UAAU,IAAI,uBAAuB,CAAC,CAAC,CAAC,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import type DynaRecord from "../../DynaRecord";
2
2
  import { TransactWriteBuilder } from "../../dynamo-utils";
3
3
  import OperationBase from "../OperationBase";
4
- import type { UpdatedAttributes, UpdateOptions } from "./types";
4
+ import type { UpdatedAttributes, UpdateOptions, UpdateOperationOptions } from "./types";
5
5
  import type { EntityClass } from "../../types";
6
6
  /**
7
7
  * Represents an update operation for a DynaRecord-backed entity, handling both attribute updates and relationship consistency via denormalization.
@@ -35,10 +35,11 @@ declare class Update<T extends DynaRecord> extends OperationBase<T> {
35
35
  *
36
36
  * @param id - The unique identifier of the entity being updated.
37
37
  * @param attributes - Partial set of entity attributes to update. Must be defined on the entity's model.
38
+ * @param options - Optional operation options including referentialIntegrityCheck flag.
38
39
  * @returns A promise that resolves to the set of updated attributes as applied to the entity.
39
40
  * @throws If the entity does not exist, an error is thrown.
40
41
  */
41
- run(id: string, attributes: UpdateOptions<DynaRecord>): Promise<UpdatedAttributes<T>>;
42
+ run(id: string, attributes: UpdateOptions<DynaRecord>, options?: UpdateOperationOptions): Promise<UpdatedAttributes<T>>;
42
43
  protected commitTransaction(): Promise<void>;
43
44
  /**
44
45
  * BelongsToRelationship meta data and foreign key value pair for foreign keys being updated
@@ -50,6 +51,8 @@ declare class Update<T extends DynaRecord> extends OperationBase<T> {
50
51
  * Adds condition checks for standalone foreign keys present in the update payload to ensure the referenced records exist.
51
52
  *
52
53
  * @param attributes - Partial entity attributes supplied to the update call.
54
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
55
+ * @private
53
56
  */
54
57
  private addStandaloneForeignKeyConditionChecks;
55
58
  /**
@@ -114,6 +117,8 @@ declare class Update<T extends DynaRecord> extends OperationBase<T> {
114
117
  * @param entityPreUpdate - The state of the entity before the update.
115
118
  * @param updatedEntity - The entity state after proposed updates (partial attributes).
116
119
  * @param updateExpression - The DynamoDB update expression representing the attribute updates.
120
+ * @param newBelongsToEntityLookup - Lookup of new belongs-to entities.
121
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
117
122
  * @private
118
123
  */
119
124
  private buildBelongsToTransactions;
@@ -141,6 +146,8 @@ declare class Update<T extends DynaRecord> extends OperationBase<T> {
141
146
  * @param newBelongsToEntityLookup
142
147
  * @param persistToSelfCondition
143
148
  * @param persistToSelfConditionErrMessage
149
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
150
+ * @private
144
151
  */
145
152
  private buildPutBelongsToLinkedRecords;
146
153
  /**
@@ -185,6 +192,8 @@ declare class Update<T extends DynaRecord> extends OperationBase<T> {
185
192
  * @param newForeignKey
186
193
  * @param oldForeignKey
187
194
  * @param newBelongsToEntityLookup
195
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
196
+ * @private
188
197
  */
189
198
  private updateForeignKeyTransactions;
190
199
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Update.d.ts","sourceRoot":"","sources":["../../../../src/operations/Update/Update.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAGL,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAmB5B,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,KAAK,EAAmB,WAAW,EAAgB,MAAM,aAAa,CAAC;AA4D9E;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAM,MAAM,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;IACzD,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;gBAG1D,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EACtB,kBAAkB,CAAC,EAAE,oBAAoB;IAO3C;;;;;;;;;;;;;OAaG;IACU,GAAG,CACd,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,aAAa,CAAC,UAAU,CAAC,GACpC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;cA6ChB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIlD;;;;OAIG;IACH,OAAO,CAAC,uCAAuC;IAY/C;;;;OAIG;IACH,OAAO,CAAC,sCAAsC;IAgC9C;;;;;;;;;;;;;;;OAeG;YACW,QAAQ;IAsDtB;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IA4B7B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,mBAAmB;IAY3B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IAyBlC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,0BAA0B;IAuDlC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,iCAAiC;IAuBzC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,8BAA8B;IA+BtC;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IAkBlC;;;;;OAKG;IACH,OAAO,CAAC,mCAAmC;IAsB3C;;;;;;;;OAQG;IACH,OAAO,CAAC,sCAAsC;IAqC9C;;;;;;;;;;OAUG;IACH,OAAO,CAAC,6BAA6B;IAuCrC;;;;;;;OAOG;IACH,OAAO,CAAC,4BAA4B;IA6BpC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,6BAA6B;IAwBrC;;;;;;OAMG;IACH,OAAO,CAAC,gCAAgC;IAgBxC;;;OAGG;IACH,OAAO,CAAC,iCAAiC;CAU1C;AAED,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Update.d.ts","sourceRoot":"","sources":["../../../../src/operations/Update/Update.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAGL,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAmB5B,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACvB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAmB,WAAW,EAAgB,MAAM,aAAa,CAAC;AA4D9E;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAM,MAAM,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;IACzD,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;gBAG1D,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EACtB,kBAAkB,CAAC,EAAE,oBAAoB;IAO3C;;;;;;;;;;;;;;OAcG;IACU,GAAG,CACd,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,aAAa,CAAC,UAAU,CAAC,EACrC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;cAoDhB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIlD;;;;OAIG;IACH,OAAO,CAAC,uCAAuC;IAY/C;;;;;;OAMG;IACH,OAAO,CAAC,sCAAsC;IAoC9C;;;;;;;;;;;;;;;OAeG;YACW,QAAQ;IAsDtB;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IA4B7B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,mBAAmB;IAY3B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IAyBlC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,0BAA0B;IA0DlC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,iCAAiC;IAuBzC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,8BAA8B;IAkCtC;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IAkBlC;;;;;OAKG;IACH,OAAO,CAAC,mCAAmC;IAsB3C;;;;;;;;OAQG;IACH,OAAO,CAAC,sCAAsC;IAqC9C;;;;;;;;;;OAUG;IACH,OAAO,CAAC,6BAA6B;IAuCrC;;;;;;;;;OASG;IACH,OAAO,CAAC,4BAA4B;IAgCpC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,6BAA6B;IAwBrC;;;;;;OAMG;IACH,OAAO,CAAC,gCAAgC;IAgBxC;;;OAGG;IACH,OAAO,CAAC,iCAAiC;CAU1C;AAED,eAAe,MAAM,CAAC"}
@@ -46,15 +46,17 @@ class Update extends OperationBase_1.default {
46
46
  *
47
47
  * @param id - The unique identifier of the entity being updated.
48
48
  * @param attributes - Partial set of entity attributes to update. Must be defined on the entity's model.
49
+ * @param options - Optional operation options including referentialIntegrityCheck flag.
49
50
  * @returns A promise that resolves to the set of updated attributes as applied to the entity.
50
51
  * @throws If the entity does not exist, an error is thrown.
51
52
  */
52
- async run(id, attributes) {
53
+ async run(id, attributes, options) {
54
+ const referentialIntegrityCheck = options?.referentialIntegrityCheck ?? true;
53
55
  const entityMeta = metadata_1.default.getEntity(this.EntityClass.name);
54
56
  const entityAttrs = entityMeta.parseRawEntityDefinedAttributesPartial(attributes);
55
57
  const { updatedAttrs, expression } = this.buildUpdateMetadata(entityAttrs);
56
58
  this.buildUpdateItemTransaction(id, expression);
57
- this.addStandaloneForeignKeyConditionChecks(entityAttrs);
59
+ this.addStandaloneForeignKeyConditionChecks(entityAttrs, referentialIntegrityCheck);
58
60
  // Only need to prefetch if the entity has relationships
59
61
  if (entityMeta.allRelationships.length > 0) {
60
62
  const belongsToRelMetaBeingUpdated = this.getBelongsToRelMetaAndKeyForUpdatedKeys(entityAttrs);
@@ -66,7 +68,7 @@ class Update extends OperationBase_1.default {
66
68
  id
67
69
  };
68
70
  this.buildUpdateRelatedEntityLinks(id, preFetch.relatedEntities, expression);
69
- this.buildBelongsToTransactions(preFetch.entityPreUpdate, updatedEntity, expression, preFetch.newBelongsToEntityLookup);
71
+ this.buildBelongsToTransactions(preFetch.entityPreUpdate, updatedEntity, expression, preFetch.newBelongsToEntityLookup, referentialIntegrityCheck);
70
72
  }
71
73
  await this.commitTransaction();
72
74
  return updatedAttrs;
@@ -91,8 +93,13 @@ class Update extends OperationBase_1.default {
91
93
  * Adds condition checks for standalone foreign keys present in the update payload to ensure the referenced records exist.
92
94
  *
93
95
  * @param attributes - Partial entity attributes supplied to the update call.
96
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
97
+ * @private
94
98
  */
95
- addStandaloneForeignKeyConditionChecks(attributes) {
99
+ addStandaloneForeignKeyConditionChecks(attributes, referentialIntegrityCheck) {
100
+ if (!referentialIntegrityCheck) {
101
+ return;
102
+ }
96
103
  const standaloneForeignKeys = this.entityMetadata.standaloneForeignKeyAttributes;
97
104
  for (const attrMeta of standaloneForeignKeys) {
98
105
  const target = attrMeta.foreignKeyTarget;
@@ -258,9 +265,11 @@ class Update extends OperationBase_1.default {
258
265
  * @param entityPreUpdate - The state of the entity before the update.
259
266
  * @param updatedEntity - The entity state after proposed updates (partial attributes).
260
267
  * @param updateExpression - The DynamoDB update expression representing the attribute updates.
268
+ * @param newBelongsToEntityLookup - Lookup of new belongs-to entities.
269
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
261
270
  * @private
262
271
  */
263
- buildBelongsToTransactions(entityPreUpdate, updatedEntity, updateExpression, newBelongsToEntityLookup) {
272
+ buildBelongsToTransactions(entityPreUpdate, updatedEntity, updateExpression, newBelongsToEntityLookup, referentialIntegrityCheck) {
264
273
  const entityId = entityPreUpdate.id;
265
274
  const relMetas = this.entityMetadata.belongsToOrOwnedByRelationships;
266
275
  for (const relMeta of relMetas) {
@@ -273,13 +282,13 @@ class Update extends OperationBase_1.default {
273
282
  const isUpdatingForeignKey = oldFk !== undefined && oldFk !== foreignKey;
274
283
  const isRemovingForeignKey = isUpdatingForeignKey && foreignKey === null;
275
284
  if (isAddingForeignKey) {
276
- this.buildPutBelongsToLinkedRecords(updatedEntity, relMeta, foreignKey, newBelongsToEntityLookup, "attribute_not_exists", `${this.EntityClass.name} already has an associated ${relMeta.target.name}`);
285
+ this.buildPutBelongsToLinkedRecords(updatedEntity, relMeta, foreignKey, newBelongsToEntityLookup, "attribute_not_exists", `${this.EntityClass.name} already has an associated ${relMeta.target.name}`, referentialIntegrityCheck);
277
286
  }
278
287
  else if (isRemovingForeignKey) {
279
288
  this.removeForeignKeysTransactions(entityId, relMeta, oldFk);
280
289
  }
281
290
  else if (isUpdatingForeignKey) {
282
- this.updateForeignKeyTransactions(updatedEntity, relMeta, foreignKey, oldFk, newBelongsToEntityLookup);
291
+ this.updateForeignKeyTransactions(updatedEntity, relMeta, foreignKey, oldFk, newBelongsToEntityLookup, referentialIntegrityCheck);
283
292
  }
284
293
  else if (foreignKey !== null) {
285
294
  this.buildUpdateBelongsToLinkedRecords(entityId, relMeta, foreignKey, updateExpression);
@@ -320,10 +329,14 @@ class Update extends OperationBase_1.default {
320
329
  * @param newBelongsToEntityLookup
321
330
  * @param persistToSelfCondition
322
331
  * @param persistToSelfConditionErrMessage
332
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
333
+ * @private
323
334
  */
324
- buildPutBelongsToLinkedRecords(updatedEntity, relMeta, foreignKey, newBelongsToEntityLookup, persistToSelfCondition, persistToSelfConditionErrMessage) {
335
+ buildPutBelongsToLinkedRecords(updatedEntity, relMeta, foreignKey, newBelongsToEntityLookup, persistToSelfCondition, persistToSelfConditionErrMessage, referentialIntegrityCheck = true) {
325
336
  // Ensure that the new foreign key is valid and exists
326
- this.buildEntityExistsCondition(relMeta, foreignKey);
337
+ if (referentialIntegrityCheck) {
338
+ this.buildEntityExistsCondition(relMeta, foreignKey);
339
+ }
327
340
  // Denormalize entity being updated to foreign partition
328
341
  this.buildLinkToForeignEntityTransaction(updatedEntity, relMeta, foreignKey);
329
342
  if ((0, utils_3.isBelongsToRelationship)(relMeta)) {
@@ -426,15 +439,17 @@ class Update extends OperationBase_1.default {
426
439
  * @param newForeignKey
427
440
  * @param oldForeignKey
428
441
  * @param newBelongsToEntityLookup
442
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
443
+ * @private
429
444
  */
430
- updateForeignKeyTransactions(updatedEntity, relMeta, newForeignKey, oldForeignKey, newBelongsToEntityLookup) {
445
+ updateForeignKeyTransactions(updatedEntity, relMeta, newForeignKey, oldForeignKey, newBelongsToEntityLookup, referentialIntegrityCheck) {
431
446
  // Keys to delete the linked record from the foreign entities partition
432
447
  const oldKeysToForeignEntity = (0, utils_2.buildBelongsToLinkKey)(this.EntityClass, updatedEntity.id, relMeta, oldForeignKey);
433
448
  this.transactionBuilder.addDelete({
434
449
  TableName: this.tableMetadata.name,
435
450
  Key: oldKeysToForeignEntity
436
451
  });
437
- this.buildPutBelongsToLinkedRecords(updatedEntity, relMeta, newForeignKey, newBelongsToEntityLookup, "attribute_exists");
452
+ this.buildPutBelongsToLinkedRecords(updatedEntity, relMeta, newForeignKey, newBelongsToEntityLookup, "attribute_exists", undefined, referentialIntegrityCheck);
438
453
  }
439
454
  /**
440
455
  * Builds transactions to update all related entities that exist in the main entity's partition, applying the same attribute updates.
@@ -28,6 +28,18 @@ type AllowNullForNullable<T> = {
28
28
  * })
29
29
  */
30
30
  export type UpdateOptions<T extends DynaRecord> = Partial<AllowNullForNullable<EntityDefinedAttributes<T>>>;
31
+ /**
32
+ * Options for update operations
33
+ */
34
+ export interface UpdateOperationOptions {
35
+ /**
36
+ * Whether to perform referential integrity checks for foreign key references.
37
+ * When `true` (default), condition checks are added to verify that referenced entities exist.
38
+ * When `false`, these condition checks are skipped, allowing updates even if foreign key references don't exist.
39
+ * @default true
40
+ */
41
+ referentialIntegrityCheck?: boolean;
42
+ }
31
43
  /**
32
44
  * Attributes of an entity that were updated. Including the auto generated updatedAt date
33
45
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/operations/Update/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD;;;;;GAKG;AACH,KAAK,kBAAkB,CAAC,CAAC,IAAI;KAC1B,CAAC,IAAI,MAAM,CAAC,GAAG,SAAS,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK;CACnD,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;;;GAKG;AACH,KAAK,oBAAoB,CAAC,CAAC,IAAI;KAC5B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,SAAS,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;CACrE,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,UAAU,IAAI,OAAO,CACvD,oBAAoB,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CACjD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,UAAU,IAAI,IAAI,CACxD,OAAO,CAAC,CAAC,CAAC,EACV,WAAW,CACZ,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/operations/Update/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD;;;;;GAKG;AACH,KAAK,kBAAkB,CAAC,CAAC,IAAI;KAC1B,CAAC,IAAI,MAAM,CAAC,GAAG,SAAS,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK;CACnD,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;;;GAKG;AACH,KAAK,oBAAoB,CAAC,CAAC,IAAI;KAC5B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,SAAS,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;CACrE,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,UAAU,IAAI,OAAO,CACvD,oBAAoB,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CACjD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,UAAU,IAAI,IAAI,CACxD,OAAO,CAAC,CAAC,CAAC,EACV,WAAW,CACZ,CAAC"}
@@ -10,6 +10,18 @@ type ExcludeKeys = "type1" | "type2";
10
10
  type ForeignKeyProperties<T> = {
11
11
  [P in Exclude<keyof T, ExcludeKeys>]: T[P] extends ForeignKey ? string : never;
12
12
  };
13
+ /**
14
+ * Options for JoinTable operations
15
+ */
16
+ interface JoinTableOptions {
17
+ /**
18
+ * Whether to perform referential integrity checks for foreign key references.
19
+ * When `true` (default), condition checks are added to verify that referenced entities exist.
20
+ * When `false`, these condition checks are skipped, allowing updates even if foreign key references don't exist.
21
+ * @default true
22
+ */
23
+ referentialIntegrityCheck?: boolean;
24
+ }
13
25
  /**
14
26
  * Abstract class representing a join table for HasAndBelongsToMany relationships.
15
27
  * This class should be extended for specific join table implementations.
@@ -33,8 +45,9 @@ declare abstract class JoinTable<T extends DynaRecord, K extends DynaRecord> {
33
45
  * Adds denormalized copy of the related entity to each associated Entity's partition
34
46
  * @param this
35
47
  * @param keys
48
+ * @param options - Optional operation options including referentialIntegrityCheck flag
36
49
  */
37
- static create<ThisClass extends JoinTable<T, K>, T extends DynaRecord, K extends DynaRecord>(this: new (type1: EntityClass<T>, type2: EntityClass<K>) => ThisClass, keys: ForeignKeyProperties<ThisClass>): Promise<void>;
50
+ static create<ThisClass extends JoinTable<T, K>, T extends DynaRecord, K extends DynaRecord>(this: new (type1: EntityClass<T>, type2: EntityClass<K>) => ThisClass, keys: ForeignKeyProperties<ThisClass>, options?: JoinTableOptions): Promise<void>;
38
51
  /**
39
52
  * Delete a JoinTable entry
40
53
  * Deletes denormalized records from each associated Entity's partition
@@ -51,6 +64,9 @@ declare abstract class JoinTable<T extends DynaRecord, K extends DynaRecord> {
51
64
  * @param keys
52
65
  * @param parentEntityMeta
53
66
  * @param linkedEntityMeta
67
+ * @param linkedRecord
68
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
69
+ * @private
54
70
  */
55
71
  private static denormalizeLinkRecord;
56
72
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"JoinTable.d.ts","sourceRoot":"","sources":["../../../src/relationships/JoinTable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAW5C,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAmB,MAAM,UAAU,CAAC;AAEzE;;GAEG;AACH,KAAK,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;AAOrC;;GAEG;AACH,KAAK,oBAAoB,CAAC,CAAC,IAAI;KAC5B,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,GACzD,MAAM,GACN,KAAK;CACV,CAAC;AAqBF;;;;;;;;;;;;;GAaG;AACH,uBAAe,SAAS,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,UAAU;IAE/D,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EACrB,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;IAGxC;;;;;OAKG;WACiB,MAAM,CACxB,SAAS,SAAS,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EACjC,CAAC,SAAS,UAAU,EACpB,CAAC,SAAS,UAAU,EAEpB,IAAI,EAAE,KAAK,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,SAAS,EACrE,IAAI,EAAE,oBAAoB,CAAC,SAAS,CAAC,GACpC,OAAO,CAAC,IAAI,CAAC;IAyBhB;;;;;OAKG;WACiB,MAAM,CACxB,SAAS,SAAS,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EACjC,CAAC,SAAS,UAAU,EACpB,CAAC,SAAS,UAAU,EAEpB,IAAI,EAAE,KAAK,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,SAAS,EACrE,IAAI,EAAE,oBAAoB,CAAC,SAAS,CAAC,GACpC,OAAO,CAAC,IAAI,CAAC;mBAWK,QAAQ;IAmD7B;;;;;;;;OAQG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IA2CpC;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAcpC;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;IA0BzB,OAAO,CAAC,MAAM,CAAC,YAAY;IAoB3B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAoB/B,OAAO,CAAC,MAAM,CAAC,4BAA4B;CA6B5C;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"JoinTable.d.ts","sourceRoot":"","sources":["../../../src/relationships/JoinTable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAW5C,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAmB,MAAM,UAAU,CAAC;AAEzE;;GAEG;AACH,KAAK,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;AAOrC;;GAEG;AACH,KAAK,oBAAoB,CAAC,CAAC,IAAI;KAC5B,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,GACzD,MAAM,GACN,KAAK;CACV,CAAC;AAYF;;GAEG;AACH,UAAU,gBAAgB;IACxB;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAWD;;;;;;;;;;;;;GAaG;AACH,uBAAe,SAAS,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,UAAU;IAE/D,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EACrB,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;IAGxC;;;;;;OAMG;WACiB,MAAM,CACxB,SAAS,SAAS,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EACjC,CAAC,SAAS,UAAU,EACpB,CAAC,SAAS,UAAU,EAEpB,IAAI,EAAE,KAAK,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,SAAS,EACrE,IAAI,EAAE,oBAAoB,CAAC,SAAS,CAAC,EACrC,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,IAAI,CAAC;IA6BhB;;;;;OAKG;WACiB,MAAM,CACxB,SAAS,SAAS,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EACjC,CAAC,SAAS,UAAU,EACpB,CAAC,SAAS,UAAU,EAEpB,IAAI,EAAE,KAAK,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,SAAS,EACrE,IAAI,EAAE,oBAAoB,CAAC,SAAS,CAAC,GACpC,OAAO,CAAC,IAAI,CAAC;mBAWK,QAAQ;IAmD7B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IA8CpC;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAcpC;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;IA0BzB,OAAO,CAAC,MAAM,CAAC,YAAY;IAoB3B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAoB/B,OAAO,CAAC,MAAM,CAAC,4BAA4B;CA6B5C;AAED,eAAe,SAAS,CAAC"}
@@ -33,14 +33,16 @@ class JoinTable {
33
33
  * Adds denormalized copy of the related entity to each associated Entity's partition
34
34
  * @param this
35
35
  * @param keys
36
+ * @param options - Optional operation options including referentialIntegrityCheck flag
36
37
  */
37
- static async create(keys) {
38
+ static async create(keys, options) {
39
+ const referentialIntegrityCheck = options?.referentialIntegrityCheck ?? true;
38
40
  const transactionBuilder = new TransactWriteBuilder_1.default();
39
41
  const [rel1, rel2] = metadata_1.default.getJoinTable(this.name);
40
42
  const transactionProps = JoinTable.transactionProps(keys, rel2, rel1);
41
43
  const lookupTableItem = await JoinTable.preFetch(transactionProps);
42
- JoinTable.denormalizeLinkRecord(transactionBuilder, keys, rel1, rel2, lookupTableItem[transactionProps.ids.linkedEntityId]);
43
- JoinTable.denormalizeLinkRecord(transactionBuilder, keys, rel2, rel1, lookupTableItem[transactionProps.ids.parentId]);
44
+ JoinTable.denormalizeLinkRecord(transactionBuilder, keys, rel1, rel2, lookupTableItem[transactionProps.ids.linkedEntityId], referentialIntegrityCheck);
45
+ JoinTable.denormalizeLinkRecord(transactionBuilder, keys, rel2, rel1, lookupTableItem[transactionProps.ids.parentId], referentialIntegrityCheck);
44
46
  await transactionBuilder.executeTransaction();
45
47
  }
46
48
  /**
@@ -90,8 +92,11 @@ class JoinTable {
90
92
  * @param keys
91
93
  * @param parentEntityMeta
92
94
  * @param linkedEntityMeta
95
+ * @param linkedRecord
96
+ * @param referentialIntegrityCheck - Whether to perform referential integrity checks.
97
+ * @private
93
98
  */
94
- static denormalizeLinkRecord(transactionBuilder, keys, parentEntityMeta, linkedEntityMeta, linkedRecord) {
99
+ static denormalizeLinkRecord(transactionBuilder, keys, parentEntityMeta, linkedEntityMeta, linkedRecord, referentialIntegrityCheck) {
95
100
  const { tableProps, entities, ids } = this.transactionProps(keys, parentEntityMeta, linkedEntityMeta);
96
101
  const { name: tableName } = tableProps;
97
102
  const { alias: partitionKeyAlias } = tableProps.partitionKeyAttribute;
@@ -105,11 +110,13 @@ class JoinTable {
105
110
  },
106
111
  ConditionExpression: `attribute_not_exists(${partitionKeyAlias})` // Ensure item doesn't already exist
107
112
  }, `${parentEntity.name} with ID ${linkedEntityId} is already linked to ${linkedEntity.name} with ID ${parentId}`);
108
- transactionBuilder.addConditionCheck({
109
- TableName: tableName,
110
- Key: this.buildForeignEntityKey(tableProps, parentEntity, linkedEntityId),
111
- ConditionExpression: `attribute_exists(${partitionKeyAlias})`
112
- }, `${parentEntity.name} with ID ${linkedEntityId} does not exist`);
113
+ if (referentialIntegrityCheck) {
114
+ transactionBuilder.addConditionCheck({
115
+ TableName: tableName,
116
+ Key: this.buildForeignEntityKey(tableProps, parentEntity, linkedEntityId),
117
+ ConditionExpression: `attribute_exists(${partitionKeyAlias})`
118
+ }, `${parentEntity.name} with ID ${linkedEntityId} does not exist`);
119
+ }
113
120
  }
114
121
  /**
115
122
  * Builds the key to the foreign entity
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dyna-record",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Typescript Data Modeler and ORM for Dynamo",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",