dyna-record 0.3.6 → 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 +56 -10
- package/dist/src/DynaRecord.d.ts +39 -7
- package/dist/src/DynaRecord.d.ts.map +1 -1
- package/dist/src/DynaRecord.js +36 -10
- package/dist/src/decorators/attributes/ForeignKeyAttribute.d.ts +10 -6
- package/dist/src/decorators/attributes/ForeignKeyAttribute.d.ts.map +1 -1
- package/dist/src/decorators/attributes/ForeignKeyAttribute.js +11 -5
- package/dist/src/decorators/relationships/BelongsTo.d.ts +3 -2
- package/dist/src/decorators/relationships/BelongsTo.d.ts.map +1 -1
- package/dist/src/decorators/relationships/types.d.ts +10 -5
- package/dist/src/decorators/relationships/types.d.ts.map +1 -1
- package/dist/src/metadata/AttributeMetadata.d.ts +4 -0
- package/dist/src/metadata/AttributeMetadata.d.ts.map +1 -1
- package/dist/src/metadata/AttributeMetadata.js +3 -0
- package/dist/src/metadata/EntityMetadata.d.ts +11 -1
- package/dist/src/metadata/EntityMetadata.d.ts.map +1 -1
- package/dist/src/metadata/EntityMetadata.js +17 -0
- package/dist/src/metadata/types.d.ts +13 -1
- package/dist/src/metadata/types.d.ts.map +1 -1
- package/dist/src/metadata/utils.d.ts +7 -1
- package/dist/src/metadata/utils.d.ts.map +1 -1
- package/dist/src/metadata/utils.js +9 -1
- package/dist/src/operations/Create/Create.d.ts +14 -3
- package/dist/src/operations/Create/Create.d.ts.map +1 -1
- package/dist/src/operations/Create/Create.js +42 -5
- package/dist/src/operations/Create/types.d.ts +12 -0
- package/dist/src/operations/Create/types.d.ts.map +1 -1
- package/dist/src/operations/Update/Update.d.ts +17 -2
- package/dist/src/operations/Update/Update.d.ts.map +1 -1
- package/dist/src/operations/Update/Update.js +52 -9
- package/dist/src/operations/Update/types.d.ts +12 -0
- package/dist/src/operations/Update/types.d.ts.map +1 -1
- package/dist/src/relationships/JoinTable.d.ts +17 -1
- package/dist/src/relationships/JoinTable.d.ts.map +1 -1
- package/dist/src/relationships/JoinTable.js +16 -9
- package/dist/src/types.d.ts +13 -3
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/metadata/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACd,MAAM,GAAG,CAAC;AACX,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/metadata/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { RelationshipMetadata, HasManyRelationship, BelongsToRelationship, HasOneRelationship, HasAndBelongsToManyRelationship, OwnedByRelationship } from ".";
|
|
1
|
+
import type { RelationshipMetadata, HasManyRelationship, BelongsToRelationship, HasOneRelationship, HasAndBelongsToManyRelationship, OwnedByRelationship, ForeignKeyAttributeMetadata } from ".";
|
|
2
2
|
import type DynaRecord from "../DynaRecord";
|
|
3
3
|
import type { BelongsToOrOwnedByRelationship, RelationshipMetadataWithForeignKey } from "./types";
|
|
4
4
|
import type { EntityClass } from "../types";
|
|
5
|
+
import type AttributeMetadata from "./AttributeMetadata";
|
|
5
6
|
/**
|
|
6
7
|
* Type guard to check if the relationship is a HasMany
|
|
7
8
|
*/
|
|
@@ -28,6 +29,11 @@ export declare const isOwnedByRelationship: (rel: RelationshipMetadata) => rel i
|
|
|
28
29
|
* @returns
|
|
29
30
|
*/
|
|
30
31
|
export declare const isRelationshipMetadataWithForeignKey: (rel: RelationshipMetadata) => rel is RelationshipMetadataWithForeignKey;
|
|
32
|
+
/**
|
|
33
|
+
* Type guard that checks whether attribute metadata represents a foreign key.
|
|
34
|
+
* Returns {@link ForeignKeyAttributeMetadata} with a guaranteed target entity.
|
|
35
|
+
*/
|
|
36
|
+
export declare const isForeignKeyAttributeMetadata: (attr: AttributeMetadata) => attr is ForeignKeyAttributeMetadata;
|
|
31
37
|
/**
|
|
32
38
|
* Returns true if an "Entity" BelongsTo the provided relationship as a HasOne
|
|
33
39
|
* @param Entity
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/metadata/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,+BAA+B,EAC/B,mBAAmB,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/metadata/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,+BAA+B,EAC/B,mBAAmB,EACnB,2BAA2B,EAC5B,MAAM,GAAG,CAAC;AACX,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EACV,8BAA8B,EAC9B,kCAAkC,EACnC,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,iBAAiB,MAAM,qBAAqB,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAC3B,oBAAoB,KACxB,GAAG,IAAI,mBAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,QAC7B,oBAAoB,KACxB,GAAG,IAAI,qBAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAC1B,oBAAoB,KACxB,GAAG,IAAI,kBAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iCAAiC,QACvC,oBAAoB,KACxB,GAAG,IAAI,+BAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAC3B,oBAAoB,KACxB,GAAG,IAAI,mBAET,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oCAAoC,QAC1C,oBAAoB,KACxB,GAAG,IAAI,kCAET,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,SAClC,iBAAiB,KACtB,IAAI,IAAI,2BAEV,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,GAAI,CAAC,SAAS,UAAU,UACxD,WAAW,CAAC,CAAC,CAAC,OACjB,8BAA8B,KAClC,OAMF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,GAAI,CAAC,SAAS,UAAU,UACzD,WAAW,CAAC,CAAC,CAAC,OACjB,8BAA8B,KAClC,OAMF,CAAC"}
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.doesEntityBelongToRelAsHasMany = exports.doesEntityBelongToRelAsHasOne = exports.isRelationshipMetadataWithForeignKey = exports.isOwnedByRelationship = exports.isHasAndBelongsToManyRelationship = exports.isHasOneRelationship = exports.isBelongsToRelationship = exports.isHasManyRelationship = void 0;
|
|
6
|
+
exports.doesEntityBelongToRelAsHasMany = exports.doesEntityBelongToRelAsHasOne = exports.isForeignKeyAttributeMetadata = exports.isRelationshipMetadataWithForeignKey = exports.isOwnedByRelationship = exports.isHasAndBelongsToManyRelationship = exports.isHasOneRelationship = exports.isBelongsToRelationship = exports.isHasManyRelationship = void 0;
|
|
7
7
|
const _1 = __importDefault(require("./"));
|
|
8
8
|
/**
|
|
9
9
|
* Type guard to check if the relationship is a HasMany
|
|
@@ -49,6 +49,14 @@ const isRelationshipMetadataWithForeignKey = (rel) => {
|
|
|
49
49
|
return "foreignKey" in rel;
|
|
50
50
|
};
|
|
51
51
|
exports.isRelationshipMetadataWithForeignKey = isRelationshipMetadataWithForeignKey;
|
|
52
|
+
/**
|
|
53
|
+
* Type guard that checks whether attribute metadata represents a foreign key.
|
|
54
|
+
* Returns {@link ForeignKeyAttributeMetadata} with a guaranteed target entity.
|
|
55
|
+
*/
|
|
56
|
+
const isForeignKeyAttributeMetadata = (attr) => {
|
|
57
|
+
return attr.foreignKeyTarget !== undefined;
|
|
58
|
+
};
|
|
59
|
+
exports.isForeignKeyAttributeMetadata = isForeignKeyAttributeMetadata;
|
|
52
60
|
/**
|
|
53
61
|
* Returns true if an "Entity" BelongsTo the provided relationship as a HasOne
|
|
54
62
|
* @param Entity
|
|
@@ -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
|
|
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,9 +76,19 @@ 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
|
+
/**
|
|
84
|
+
* Adds DynamoDB condition checks for foreign keys that are not associated with a relationship decorator.
|
|
85
|
+
* Ensures referenced entities exist even when denormalised access patterns are not defined.
|
|
86
|
+
*
|
|
87
|
+
* @param entityData - Attributes being persisted for the new entity.
|
|
88
|
+
* @param referentialIntegrityCheck - Whether to perform referential integrity checks.
|
|
89
|
+
* @private
|
|
90
|
+
*/
|
|
91
|
+
private buildStandaloneForeignKeyConditionChecks;
|
|
81
92
|
/**
|
|
82
93
|
* Retrieves the DynamoDB items for all entities that the new entity references via "BelongsTo" relationships.
|
|
83
94
|
*
|
|
@@ -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;
|
|
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,17 +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);
|
|
61
|
+
this.buildBelongsToTransactions(entityData, tableItem, referentialIntegrityCheck);
|
|
62
|
+
this.buildStandaloneForeignKeyConditionChecks(entityData, referentialIntegrityCheck);
|
|
60
63
|
// Attempt to fetch all belongs-to entities to properly create reverse denormalization links
|
|
61
64
|
const belongsToTableItems = await this.getBelongsToTableItems(entityData);
|
|
62
65
|
// If there are any belongs-to relationships, add the inverse link records into the new entity's partition
|
|
@@ -126,16 +129,19 @@ class Create extends OperationBase_1.default {
|
|
|
126
129
|
*
|
|
127
130
|
* @param entityData - The complete set of entity attributes for the new entity.
|
|
128
131
|
* @param tableItem - The main entity's DynamoDB table item.
|
|
132
|
+
* @param referentialIntegrityCheck - Whether to perform referential integrity checks.
|
|
129
133
|
* @private
|
|
130
134
|
*/
|
|
131
|
-
buildBelongsToTransactions(entityData, tableItem) {
|
|
135
|
+
buildBelongsToTransactions(entityData, tableItem, referentialIntegrityCheck) {
|
|
132
136
|
const tableName = this.tableMetadata.name;
|
|
133
137
|
const relMetadata = this.entityMetadata.belongsToOrOwnedByRelationships;
|
|
134
138
|
for (const relMeta of relMetadata) {
|
|
135
139
|
const foreignKey = (0, utils_2.extractForeignKeyFromEntity)(relMeta, entityData);
|
|
136
140
|
if (foreignKey !== undefined) {
|
|
137
141
|
// Ensure referenced entity exists before linking
|
|
138
|
-
|
|
142
|
+
if (referentialIntegrityCheck) {
|
|
143
|
+
this.buildRelationshipExistsConditionTransaction(relMeta, foreignKey);
|
|
144
|
+
}
|
|
139
145
|
const key = (0, utils_2.buildBelongsToLinkKey)(this.EntityClass, entityData.id, relMeta, foreignKey);
|
|
140
146
|
this.#transactionBuilder.addPut({
|
|
141
147
|
TableName: tableName,
|
|
@@ -145,6 +151,37 @@ class Create extends OperationBase_1.default {
|
|
|
145
151
|
}
|
|
146
152
|
}
|
|
147
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Adds DynamoDB condition checks for foreign keys that are not associated with a relationship decorator.
|
|
156
|
+
* Ensures referenced entities exist even when denormalised access patterns are not defined.
|
|
157
|
+
*
|
|
158
|
+
* @param entityData - Attributes being persisted for the new entity.
|
|
159
|
+
* @param referentialIntegrityCheck - Whether to perform referential integrity checks.
|
|
160
|
+
* @private
|
|
161
|
+
*/
|
|
162
|
+
buildStandaloneForeignKeyConditionChecks(entityData, referentialIntegrityCheck) {
|
|
163
|
+
if (!referentialIntegrityCheck) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const standaloneForeignKeys = this.entityMetadata.standaloneForeignKeyAttributes;
|
|
167
|
+
for (const attrMeta of standaloneForeignKeys) {
|
|
168
|
+
const target = attrMeta.foreignKeyTarget;
|
|
169
|
+
const foreignKeyValue = entityData[attrMeta.name];
|
|
170
|
+
if (!(0, utils_1.isString)(foreignKeyValue))
|
|
171
|
+
continue;
|
|
172
|
+
const foreignKey = foreignKeyValue;
|
|
173
|
+
const errMsg = `${target.name} with ID '${foreignKey}' does not exist`;
|
|
174
|
+
const conditionCheck = {
|
|
175
|
+
TableName: this.tableMetadata.name,
|
|
176
|
+
Key: {
|
|
177
|
+
[this.partitionKeyAlias]: target.partitionKeyValue(foreignKey),
|
|
178
|
+
[this.sortKeyAlias]: target.name
|
|
179
|
+
},
|
|
180
|
+
ConditionExpression: `attribute_exists(${this.partitionKeyAlias})`
|
|
181
|
+
};
|
|
182
|
+
this.#transactionBuilder.addConditionCheck(conditionCheck, errMsg);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
148
185
|
/**
|
|
149
186
|
* Retrieves the DynamoDB items for all entities that the new entity references via "BelongsTo" relationships.
|
|
150
187
|
*
|
|
@@ -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
|
|
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
|
|
@@ -46,6 +47,14 @@ declare class Update<T extends DynaRecord> extends OperationBase<T> {
|
|
|
46
47
|
* @returns
|
|
47
48
|
*/
|
|
48
49
|
private getBelongsToRelMetaAndKeyForUpdatedKeys;
|
|
50
|
+
/**
|
|
51
|
+
* Adds condition checks for standalone foreign keys present in the update payload to ensure the referenced records exist.
|
|
52
|
+
*
|
|
53
|
+
* @param attributes - Partial entity attributes supplied to the update call.
|
|
54
|
+
* @param referentialIntegrityCheck - Whether to perform referential integrity checks.
|
|
55
|
+
* @private
|
|
56
|
+
*/
|
|
57
|
+
private addStandaloneForeignKeyConditionChecks;
|
|
49
58
|
/**
|
|
50
59
|
* Pre-fetches the target entity and any related entities from the database. This is done using a strong read operation to ensure consistency.
|
|
51
60
|
*
|
|
@@ -108,6 +117,8 @@ declare class Update<T extends DynaRecord> extends OperationBase<T> {
|
|
|
108
117
|
* @param entityPreUpdate - The state of the entity before the update.
|
|
109
118
|
* @param updatedEntity - The entity state after proposed updates (partial attributes).
|
|
110
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.
|
|
111
122
|
* @private
|
|
112
123
|
*/
|
|
113
124
|
private buildBelongsToTransactions;
|
|
@@ -135,6 +146,8 @@ declare class Update<T extends DynaRecord> extends OperationBase<T> {
|
|
|
135
146
|
* @param newBelongsToEntityLookup
|
|
136
147
|
* @param persistToSelfCondition
|
|
137
148
|
* @param persistToSelfConditionErrMessage
|
|
149
|
+
* @param referentialIntegrityCheck - Whether to perform referential integrity checks.
|
|
150
|
+
* @private
|
|
138
151
|
*/
|
|
139
152
|
private buildPutBelongsToLinkedRecords;
|
|
140
153
|
/**
|
|
@@ -179,6 +192,8 @@ declare class Update<T extends DynaRecord> extends OperationBase<T> {
|
|
|
179
192
|
* @param newForeignKey
|
|
180
193
|
* @param oldForeignKey
|
|
181
194
|
* @param newBelongsToEntityLookup
|
|
195
|
+
* @param referentialIntegrityCheck - Whether to perform referential integrity checks.
|
|
196
|
+
* @private
|
|
182
197
|
*/
|
|
183
198
|
private updateForeignKeyTransactions;
|
|
184
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;
|
|
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,14 +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);
|
|
59
|
+
this.addStandaloneForeignKeyConditionChecks(entityAttrs, referentialIntegrityCheck);
|
|
57
60
|
// Only need to prefetch if the entity has relationships
|
|
58
61
|
if (entityMeta.allRelationships.length > 0) {
|
|
59
62
|
const belongsToRelMetaBeingUpdated = this.getBelongsToRelMetaAndKeyForUpdatedKeys(entityAttrs);
|
|
@@ -65,7 +68,7 @@ class Update extends OperationBase_1.default {
|
|
|
65
68
|
id
|
|
66
69
|
};
|
|
67
70
|
this.buildUpdateRelatedEntityLinks(id, preFetch.relatedEntities, expression);
|
|
68
|
-
this.buildBelongsToTransactions(preFetch.entityPreUpdate, updatedEntity, expression, preFetch.newBelongsToEntityLookup);
|
|
71
|
+
this.buildBelongsToTransactions(preFetch.entityPreUpdate, updatedEntity, expression, preFetch.newBelongsToEntityLookup, referentialIntegrityCheck);
|
|
69
72
|
}
|
|
70
73
|
await this.commitTransaction();
|
|
71
74
|
return updatedAttrs;
|
|
@@ -86,6 +89,38 @@ class Update extends OperationBase_1.default {
|
|
|
86
89
|
return acc;
|
|
87
90
|
}, []);
|
|
88
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Adds condition checks for standalone foreign keys present in the update payload to ensure the referenced records exist.
|
|
94
|
+
*
|
|
95
|
+
* @param attributes - Partial entity attributes supplied to the update call.
|
|
96
|
+
* @param referentialIntegrityCheck - Whether to perform referential integrity checks.
|
|
97
|
+
* @private
|
|
98
|
+
*/
|
|
99
|
+
addStandaloneForeignKeyConditionChecks(attributes, referentialIntegrityCheck) {
|
|
100
|
+
if (!referentialIntegrityCheck) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const standaloneForeignKeys = this.entityMetadata.standaloneForeignKeyAttributes;
|
|
104
|
+
for (const attrMeta of standaloneForeignKeys) {
|
|
105
|
+
const target = attrMeta.foreignKeyTarget;
|
|
106
|
+
if (!(0, utils_1.isKeyOfObject)(attributes, attrMeta.name))
|
|
107
|
+
continue;
|
|
108
|
+
const foreignKeyValue = attributes[attrMeta.name];
|
|
109
|
+
if (!(0, utils_1.isString)(foreignKeyValue))
|
|
110
|
+
continue;
|
|
111
|
+
const foreignKey = foreignKeyValue;
|
|
112
|
+
const errMsg = `${target.name} with ID '${foreignKey}' does not exist`;
|
|
113
|
+
const conditionCheck = {
|
|
114
|
+
TableName: this.tableMetadata.name,
|
|
115
|
+
Key: {
|
|
116
|
+
[this.partitionKeyAlias]: target.partitionKeyValue(foreignKey),
|
|
117
|
+
[this.sortKeyAlias]: target.name
|
|
118
|
+
},
|
|
119
|
+
ConditionExpression: `attribute_exists(${this.partitionKeyAlias})`
|
|
120
|
+
};
|
|
121
|
+
this.transactionBuilder.addConditionCheck(conditionCheck, errMsg);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
89
124
|
/**
|
|
90
125
|
* Pre-fetches the target entity and any related entities from the database. This is done using a strong read operation to ensure consistency.
|
|
91
126
|
*
|
|
@@ -230,9 +265,11 @@ class Update extends OperationBase_1.default {
|
|
|
230
265
|
* @param entityPreUpdate - The state of the entity before the update.
|
|
231
266
|
* @param updatedEntity - The entity state after proposed updates (partial attributes).
|
|
232
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.
|
|
233
270
|
* @private
|
|
234
271
|
*/
|
|
235
|
-
buildBelongsToTransactions(entityPreUpdate, updatedEntity, updateExpression, newBelongsToEntityLookup) {
|
|
272
|
+
buildBelongsToTransactions(entityPreUpdate, updatedEntity, updateExpression, newBelongsToEntityLookup, referentialIntegrityCheck) {
|
|
236
273
|
const entityId = entityPreUpdate.id;
|
|
237
274
|
const relMetas = this.entityMetadata.belongsToOrOwnedByRelationships;
|
|
238
275
|
for (const relMeta of relMetas) {
|
|
@@ -245,13 +282,13 @@ class Update extends OperationBase_1.default {
|
|
|
245
282
|
const isUpdatingForeignKey = oldFk !== undefined && oldFk !== foreignKey;
|
|
246
283
|
const isRemovingForeignKey = isUpdatingForeignKey && foreignKey === null;
|
|
247
284
|
if (isAddingForeignKey) {
|
|
248
|
-
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);
|
|
249
286
|
}
|
|
250
287
|
else if (isRemovingForeignKey) {
|
|
251
288
|
this.removeForeignKeysTransactions(entityId, relMeta, oldFk);
|
|
252
289
|
}
|
|
253
290
|
else if (isUpdatingForeignKey) {
|
|
254
|
-
this.updateForeignKeyTransactions(updatedEntity, relMeta, foreignKey, oldFk, newBelongsToEntityLookup);
|
|
291
|
+
this.updateForeignKeyTransactions(updatedEntity, relMeta, foreignKey, oldFk, newBelongsToEntityLookup, referentialIntegrityCheck);
|
|
255
292
|
}
|
|
256
293
|
else if (foreignKey !== null) {
|
|
257
294
|
this.buildUpdateBelongsToLinkedRecords(entityId, relMeta, foreignKey, updateExpression);
|
|
@@ -292,10 +329,14 @@ class Update extends OperationBase_1.default {
|
|
|
292
329
|
* @param newBelongsToEntityLookup
|
|
293
330
|
* @param persistToSelfCondition
|
|
294
331
|
* @param persistToSelfConditionErrMessage
|
|
332
|
+
* @param referentialIntegrityCheck - Whether to perform referential integrity checks.
|
|
333
|
+
* @private
|
|
295
334
|
*/
|
|
296
|
-
buildPutBelongsToLinkedRecords(updatedEntity, relMeta, foreignKey, newBelongsToEntityLookup, persistToSelfCondition, persistToSelfConditionErrMessage) {
|
|
335
|
+
buildPutBelongsToLinkedRecords(updatedEntity, relMeta, foreignKey, newBelongsToEntityLookup, persistToSelfCondition, persistToSelfConditionErrMessage, referentialIntegrityCheck = true) {
|
|
297
336
|
// Ensure that the new foreign key is valid and exists
|
|
298
|
-
|
|
337
|
+
if (referentialIntegrityCheck) {
|
|
338
|
+
this.buildEntityExistsCondition(relMeta, foreignKey);
|
|
339
|
+
}
|
|
299
340
|
// Denormalize entity being updated to foreign partition
|
|
300
341
|
this.buildLinkToForeignEntityTransaction(updatedEntity, relMeta, foreignKey);
|
|
301
342
|
if ((0, utils_3.isBelongsToRelationship)(relMeta)) {
|
|
@@ -398,15 +439,17 @@ class Update extends OperationBase_1.default {
|
|
|
398
439
|
* @param newForeignKey
|
|
399
440
|
* @param oldForeignKey
|
|
400
441
|
* @param newBelongsToEntityLookup
|
|
442
|
+
* @param referentialIntegrityCheck - Whether to perform referential integrity checks.
|
|
443
|
+
* @private
|
|
401
444
|
*/
|
|
402
|
-
updateForeignKeyTransactions(updatedEntity, relMeta, newForeignKey, oldForeignKey, newBelongsToEntityLookup) {
|
|
445
|
+
updateForeignKeyTransactions(updatedEntity, relMeta, newForeignKey, oldForeignKey, newBelongsToEntityLookup, referentialIntegrityCheck) {
|
|
403
446
|
// Keys to delete the linked record from the foreign entities partition
|
|
404
447
|
const oldKeysToForeignEntity = (0, utils_2.buildBelongsToLinkKey)(this.EntityClass, updatedEntity.id, relMeta, oldForeignKey);
|
|
405
448
|
this.transactionBuilder.addDelete({
|
|
406
449
|
TableName: this.tableMetadata.name,
|
|
407
450
|
Key: oldKeysToForeignEntity
|
|
408
451
|
});
|
|
409
|
-
this.buildPutBelongsToLinkedRecords(updatedEntity, relMeta, newForeignKey, newBelongsToEntityLookup, "attribute_exists");
|
|
452
|
+
this.buildPutBelongsToLinkedRecords(updatedEntity, relMeta, newForeignKey, newBelongsToEntityLookup, "attribute_exists", undefined, referentialIntegrityCheck);
|
|
410
453
|
}
|
|
411
454
|
/**
|
|
412
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
|
|
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;
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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/dist/src/types.d.ts
CHANGED
|
@@ -16,13 +16,23 @@ export type SortKey = Brand<string, "SortKey">;
|
|
|
16
16
|
*/
|
|
17
17
|
export type PartitionKey = Brand<string, "PartitionKey">;
|
|
18
18
|
/**
|
|
19
|
-
* A branded string type to represent foreign keys in DynamoDB tables
|
|
19
|
+
* A branded string type to represent foreign keys in DynamoDB tables.
|
|
20
|
+
*
|
|
21
|
+
* @typeParam T - The entity that the foreign key references. Defaults to {@link DynaRecord}.
|
|
20
22
|
*/
|
|
21
|
-
export type ForeignKey = Brand<string,
|
|
23
|
+
export type ForeignKey<T extends DynaRecord = DynaRecord> = Brand<string, {
|
|
24
|
+
kind: "ForeignKey";
|
|
25
|
+
entity: T;
|
|
26
|
+
}>;
|
|
22
27
|
/**
|
|
23
28
|
* A branded string type to represent nullable foreign keys in DynamoDB tables, which can also be undefined.
|
|
29
|
+
*
|
|
30
|
+
* @typeParam T - The entity that the foreign key references. Defaults to {@link DynaRecord}.
|
|
24
31
|
*/
|
|
25
|
-
export type NullableForeignKey = Optional<Brand<string,
|
|
32
|
+
export type NullableForeignKey<T extends DynaRecord = DynaRecord> = Optional<Brand<string, {
|
|
33
|
+
kind: "NullableForeignKey";
|
|
34
|
+
entity: T;
|
|
35
|
+
}>>;
|
|
26
36
|
/**
|
|
27
37
|
* Represents a foreign key property on an entity within a DynaRecord model
|
|
28
38
|
*/
|
package/dist/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAEzD
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAEzD;;;;GAIG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,IAAI,KAAK,CAC/D,MAAM,EACN;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAClC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,IAAI,QAAQ,CAC1E,KAAK,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC,CACzD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,UAAU,GAAG,UAAU,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;AAExC;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEnC;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,kBAAkB,CAAC;IACpC,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;CACjD;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GACzD,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAEtB;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,OAAO,UAAU,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;KAC3D,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACjB,CAAC"}
|