@aws-amplify/datastore 3.7.4-custom-pk.79 → 3.7.4-custom-pk.87
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/dist/aws-amplify-datastore.js +458 -256
- package/dist/aws-amplify-datastore.js.map +1 -1
- package/dist/aws-amplify-datastore.min.js +3 -3
- package/dist/aws-amplify-datastore.min.js.map +1 -1
- package/lib/datastore/datastore.js +47 -11
- package/lib/datastore/datastore.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageAdapter.js +229 -135
- package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageDatabase.js +3 -2
- package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.js +12 -17
- package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib/sync/utils.js +7 -3
- package/lib/sync/utils.js.map +1 -1
- package/lib/types.d.ts +5 -1
- package/lib/types.js.map +1 -1
- package/lib/util.d.ts +7 -2
- package/lib/util.js +76 -54
- package/lib/util.js.map +1 -1
- package/lib-esm/datastore/datastore.js +48 -12
- package/lib-esm/datastore/datastore.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js +229 -135
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js +4 -3
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +12 -17
- package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib-esm/sync/utils.js +7 -3
- package/lib-esm/sync/utils.js.map +1 -1
- package/lib-esm/types.d.ts +5 -1
- package/lib-esm/types.js.map +1 -1
- package/lib-esm/util.d.ts +7 -2
- package/lib-esm/util.js +74 -54
- package/lib-esm/util.js.map +1 -1
- package/package.json +7 -7
- package/src/datastore/datastore.ts +60 -13
- package/src/storage/adapter/AsyncStorageAdapter.ts +162 -35
- package/src/storage/adapter/AsyncStorageDatabase.ts +4 -2
- package/src/storage/adapter/IndexedDBAdapter.ts +14 -25
- package/src/sync/utils.ts +7 -4
- package/src/types.ts +10 -1
- package/src/util.ts +71 -35
|
@@ -65,8 +65,9 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
65
65
|
|
|
66
66
|
// Returns primary keys for a model
|
|
67
67
|
private getIndexKeys(namespaceName: string, modelName: string): string[] {
|
|
68
|
-
const
|
|
69
|
-
|
|
68
|
+
const namespace = this.schema.namespaces[namespaceName];
|
|
69
|
+
|
|
70
|
+
const keyPath = namespace?.keys[modelName]?.primaryKey;
|
|
70
71
|
|
|
71
72
|
if (keyPath) {
|
|
72
73
|
return keyPath;
|
|
@@ -239,7 +240,8 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
239
240
|
}
|
|
240
241
|
|
|
241
242
|
for await (const relation of relations) {
|
|
242
|
-
const { fieldName, modelName, targetName, relationType } =
|
|
243
|
+
const { fieldName, modelName, targetName, targetNames, relationType } =
|
|
244
|
+
relation;
|
|
243
245
|
const storeName = this.getStorename(namespaceName, modelName);
|
|
244
246
|
const modelConstructor = this.getModelConstructorByModelName(
|
|
245
247
|
namespaceName,
|
|
@@ -249,22 +251,84 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
249
251
|
switch (relationType) {
|
|
250
252
|
case 'HAS_ONE':
|
|
251
253
|
for await (const recordItem of records) {
|
|
252
|
-
|
|
253
|
-
if (
|
|
254
|
+
// ASYNC CPK TODO: make this cleaner
|
|
255
|
+
if (targetNames?.length) {
|
|
256
|
+
let getByFields = [];
|
|
257
|
+
let allPresent;
|
|
258
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
259
|
+
allPresent = targetNames.every(targetName => {
|
|
260
|
+
return recordItem[targetName] != null;
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
if (!allPresent) {
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
getByFields = targetNames as any;
|
|
268
|
+
|
|
269
|
+
// keys are the key values
|
|
270
|
+
const keys = getByFields
|
|
271
|
+
.map(getByField => recordItem[getByField])
|
|
272
|
+
.join(DEFAULT_PRIMARY_KEY_SEPARATOR);
|
|
273
|
+
|
|
274
|
+
const connectionRecord = await this.db.get(
|
|
275
|
+
keys as any,
|
|
276
|
+
storeName
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
recordItem[fieldName] =
|
|
280
|
+
connectionRecord &&
|
|
281
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
282
|
+
} else {
|
|
283
|
+
const getByfield = recordItem[targetName]
|
|
284
|
+
? targetName
|
|
285
|
+
: fieldName;
|
|
286
|
+
if (!recordItem[getByfield]) break;
|
|
254
287
|
|
|
255
|
-
|
|
288
|
+
const key = recordItem[getByfield];
|
|
256
289
|
|
|
257
|
-
|
|
290
|
+
const connectionRecord = await this.db.get(key, storeName);
|
|
258
291
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
292
|
+
recordItem[fieldName] =
|
|
293
|
+
connectionRecord &&
|
|
294
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
295
|
+
}
|
|
262
296
|
}
|
|
263
297
|
|
|
264
298
|
break;
|
|
265
299
|
case 'BELONGS_TO':
|
|
266
300
|
for await (const recordItem of records) {
|
|
267
|
-
|
|
301
|
+
// ASYNC CPK TODO: make this cleaner
|
|
302
|
+
if (targetNames?.length) {
|
|
303
|
+
let allPresent;
|
|
304
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
305
|
+
allPresent = targetNames.every(targetName => {
|
|
306
|
+
return recordItem[targetName] != null;
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// If not present, there is not yet a connected record
|
|
310
|
+
if (!allPresent) {
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const keys = targetNames
|
|
315
|
+
.map(targetName => recordItem[targetName])
|
|
316
|
+
.join(DEFAULT_PRIMARY_KEY_SEPARATOR);
|
|
317
|
+
|
|
318
|
+
// Retrieve the connected record
|
|
319
|
+
const connectionRecord = await this.db.get(
|
|
320
|
+
keys as any,
|
|
321
|
+
storeName
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
recordItem[fieldName] =
|
|
325
|
+
connectionRecord &&
|
|
326
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
327
|
+
|
|
328
|
+
targetNames?.map(targetName => {
|
|
329
|
+
delete recordItem[targetName];
|
|
330
|
+
});
|
|
331
|
+
} else if (recordItem[targetName as any]) {
|
|
268
332
|
const key = recordItem[targetName];
|
|
269
333
|
|
|
270
334
|
const connectionRecord = await this.db.get(key, storeName);
|
|
@@ -600,36 +664,99 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
600
664
|
switch (relationType) {
|
|
601
665
|
case 'HAS_ONE':
|
|
602
666
|
for await (const model of models) {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
667
|
+
if (targetNames && targetNames?.length) {
|
|
668
|
+
const hasOneIndex = index || associatedWith;
|
|
669
|
+
|
|
670
|
+
// iterate over targetNames array and see if each item is present in model object
|
|
671
|
+
// targetNames here being the keys for the CHILD model
|
|
672
|
+
const hasConnectedModelFields = targetNames.every(targetName =>
|
|
673
|
+
model.hasOwnProperty(targetName)
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
// PK / Composite key for the parent model
|
|
677
|
+
const keyValuesPath: string = this.getIndexKeyValuesPath(model);
|
|
678
|
+
|
|
679
|
+
let values;
|
|
680
|
+
|
|
681
|
+
if (hasConnectedModelFields) {
|
|
682
|
+
// Values will be that of the child model
|
|
683
|
+
values = targetNames.map(
|
|
684
|
+
targetName => model[targetName]
|
|
685
|
+
) as any;
|
|
686
|
+
} else {
|
|
687
|
+
// values will be that of the parent model
|
|
688
|
+
values = keyValuesPath;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (values.length === 0) break;
|
|
692
|
+
|
|
693
|
+
const allRecords = await this.db.getAll(storeName);
|
|
694
|
+
|
|
695
|
+
let recordToDelete;
|
|
696
|
+
|
|
697
|
+
// values === targetNames
|
|
698
|
+
if (hasConnectedModelFields) {
|
|
699
|
+
/**
|
|
700
|
+
* Retrieve record by finding the record where all
|
|
701
|
+
* targetNames are present on the connected model
|
|
702
|
+
*/
|
|
703
|
+
recordToDelete = allRecords.filter(childItem =>
|
|
704
|
+
values.every(value => childItem[value] != null)
|
|
705
|
+
) as T[];
|
|
706
|
+
} else {
|
|
707
|
+
// values === keyValuePath
|
|
708
|
+
recordToDelete = allRecords.filter(
|
|
709
|
+
childItem => childItem[hasOneIndex] === values
|
|
710
|
+
) as T[];
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
await this.deleteTraverse<T>(
|
|
714
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
715
|
+
.relationTypes,
|
|
716
|
+
recordToDelete,
|
|
717
|
+
modelName,
|
|
718
|
+
nameSpace,
|
|
719
|
+
deleteQueue
|
|
720
|
+
);
|
|
721
|
+
} else {
|
|
722
|
+
const hasOneIndex = index || associatedWith;
|
|
723
|
+
const hasOneCustomField = targetName in model;
|
|
724
|
+
const keyValuesPath: string = this.getIndexKeyValuesPath(model);
|
|
725
|
+
const value = hasOneCustomField
|
|
726
|
+
? model[targetName]
|
|
727
|
+
: keyValuesPath;
|
|
728
|
+
|
|
729
|
+
if (!value) break;
|
|
730
|
+
|
|
731
|
+
const allRecords = await this.db.getAll(storeName);
|
|
732
|
+
|
|
733
|
+
const recordToDelete = allRecords.filter(
|
|
734
|
+
childItem => childItem[hasOneIndex] === value
|
|
735
|
+
) as T[];
|
|
736
|
+
|
|
737
|
+
await this.deleteTraverse<T>(
|
|
738
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
739
|
+
.relationTypes,
|
|
740
|
+
recordToDelete,
|
|
741
|
+
modelName,
|
|
742
|
+
nameSpace,
|
|
743
|
+
deleteQueue
|
|
744
|
+
);
|
|
745
|
+
}
|
|
624
746
|
}
|
|
625
747
|
break;
|
|
626
748
|
case 'HAS_MANY':
|
|
627
749
|
for await (const model of models) {
|
|
628
|
-
|
|
750
|
+
// Key values for the parent model:
|
|
751
|
+
const keyValues: string[] = this.getIndexKeyValues(model);
|
|
629
752
|
|
|
630
753
|
const allRecords = await this.db.getAll(storeName);
|
|
631
|
-
|
|
632
|
-
|
|
754
|
+
|
|
755
|
+
// Use constant if we go with this approach
|
|
756
|
+
const indices = index.split('-');
|
|
757
|
+
|
|
758
|
+
const childrenArray = allRecords.filter(childItem =>
|
|
759
|
+
indices.every(index => keyValues.includes(childItem[index]))
|
|
633
760
|
) as T[];
|
|
634
761
|
|
|
635
762
|
await this.deleteTraverse<T>(
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
PersistentModel,
|
|
7
7
|
QueryOne,
|
|
8
8
|
} from '../../types';
|
|
9
|
-
import { monotonicUlidFactory } from '../../util';
|
|
9
|
+
import { indexNameFromKeys, monotonicUlidFactory } from '../../util';
|
|
10
10
|
import { createInMemoryStore } from './InMemoryStore';
|
|
11
11
|
|
|
12
12
|
const DB_NAME = '@AmplifyDatastore';
|
|
@@ -96,8 +96,10 @@ class AsyncStorageDatabase {
|
|
|
96
96
|
keys: string[],
|
|
97
97
|
keyValuesPath: string
|
|
98
98
|
) {
|
|
99
|
+
const idxName = indexNameFromKeys(keys);
|
|
100
|
+
|
|
99
101
|
const ulid =
|
|
100
|
-
this.getCollectionIndex(storeName).get(
|
|
102
|
+
this.getCollectionIndex(storeName).get(idxName) ||
|
|
101
103
|
this.getMonotonicFactory(storeName)();
|
|
102
104
|
|
|
103
105
|
// Retrieve db key for item
|
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
traverseModel,
|
|
30
30
|
validatePredicate,
|
|
31
31
|
sortCompareFunction,
|
|
32
|
-
extractTargetNamesFromSrc,
|
|
33
32
|
} from '../../util';
|
|
34
33
|
import { Adapter } from './index';
|
|
35
34
|
|
|
@@ -88,8 +87,9 @@ class IndexedDBAdapter implements Adapter {
|
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
private getIndexKeyPath(namespaceName: string, modelName: string): string[] {
|
|
91
|
-
const
|
|
92
|
-
|
|
90
|
+
const namespace = this.schema.namespaces[namespaceName];
|
|
91
|
+
|
|
92
|
+
const keyPath = namespace?.keys[modelName]?.primaryKey;
|
|
93
93
|
|
|
94
94
|
if (keyPath) {
|
|
95
95
|
return keyPath;
|
|
@@ -102,6 +102,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
102
102
|
const modelConstructor = Object.getPrototypeOf(model)
|
|
103
103
|
.constructor as PersistentModelConstructor<T>;
|
|
104
104
|
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
105
|
+
|
|
105
106
|
const keys = this.getIndexKeyPath(namespaceName, modelConstructor.name);
|
|
106
107
|
|
|
107
108
|
const keyValues = keys.map(field => model[field]);
|
|
@@ -860,7 +861,6 @@ class IndexedDBAdapter implements Adapter {
|
|
|
860
861
|
|
|
861
862
|
const storeName = this.getStorename(nameSpace, modelName);
|
|
862
863
|
|
|
863
|
-
// Explain:
|
|
864
864
|
const index: string | string[] | undefined =
|
|
865
865
|
getIndex(
|
|
866
866
|
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
@@ -909,7 +909,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
909
909
|
const recordToDelete = <T>await this.db
|
|
910
910
|
.transaction(storeName, 'readwrite')
|
|
911
911
|
.objectStore(storeName)
|
|
912
|
-
.index(hasOneIndex as
|
|
912
|
+
.index(hasOneIndex as string)
|
|
913
913
|
.get(values);
|
|
914
914
|
|
|
915
915
|
await this.deleteTraverse(
|
|
@@ -927,19 +927,14 @@ class IndexedDBAdapter implements Adapter {
|
|
|
927
927
|
|
|
928
928
|
const hasOneCustomField = targetName in model;
|
|
929
929
|
const keyValues = this.getIndexKeyValues(model);
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
if (hasOneIndex === 'byPk') {
|
|
933
|
-
// byPk requires an array keyValue
|
|
934
|
-
value = [value];
|
|
935
|
-
}
|
|
930
|
+
const value = hasOneCustomField ? [model[targetName]] : keyValues;
|
|
936
931
|
|
|
937
932
|
if (!value) break;
|
|
938
933
|
|
|
939
934
|
const recordToDelete = <T>await this.db
|
|
940
935
|
.transaction(storeName, 'readwrite')
|
|
941
936
|
.objectStore(storeName)
|
|
942
|
-
.index(hasOneIndex as
|
|
937
|
+
.index(hasOneIndex as string)
|
|
943
938
|
.get(value);
|
|
944
939
|
|
|
945
940
|
await this.deleteTraverse(
|
|
@@ -957,16 +952,11 @@ class IndexedDBAdapter implements Adapter {
|
|
|
957
952
|
for await (const model of models) {
|
|
958
953
|
const keyValues = this.getIndexKeyValues(model);
|
|
959
954
|
|
|
960
|
-
// CPK TODO: Double check this, unsure that we encounter `byPk` here, ever.
|
|
961
|
-
const hasManyIndex = index || 'byPk';
|
|
962
|
-
|
|
963
|
-
// CPK TODO: get by all key values
|
|
964
955
|
const childrenArray = await this.db
|
|
965
956
|
.transaction(storeName, 'readwrite')
|
|
966
957
|
.objectStore(storeName)
|
|
967
|
-
.index(
|
|
968
|
-
.getAll(keyValues
|
|
969
|
-
// .getAll(keyValues); // TODO
|
|
958
|
+
.index(index as string)
|
|
959
|
+
.getAll(keyValues);
|
|
970
960
|
|
|
971
961
|
await this.deleteTraverse(
|
|
972
962
|
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
@@ -1080,13 +1070,12 @@ class IndexedDBAdapter implements Adapter {
|
|
|
1080
1070
|
autoIncrement: true,
|
|
1081
1071
|
});
|
|
1082
1072
|
|
|
1083
|
-
const indexes =
|
|
1084
|
-
this.schema.namespaces[namespaceName].relationships[modelName]
|
|
1085
|
-
indexes.forEach(index => store.createIndex(index, index));
|
|
1073
|
+
const { indexes } =
|
|
1074
|
+
this.schema.namespaces[namespaceName].relationships[modelName];
|
|
1086
1075
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1076
|
+
indexes.forEach(([idxName, keyPath, options]) => {
|
|
1077
|
+
store.createIndex(idxName, keyPath, options);
|
|
1078
|
+
});
|
|
1090
1079
|
}
|
|
1091
1080
|
}
|
|
1092
1081
|
|
package/src/sync/utils.ts
CHANGED
|
@@ -147,7 +147,7 @@ function getConnectionFields(
|
|
|
147
147
|
modelDefinition: SchemaModel,
|
|
148
148
|
namespace: SchemaNamespace
|
|
149
149
|
): string[] {
|
|
150
|
-
const result = [];
|
|
150
|
+
const result: string[] = [];
|
|
151
151
|
|
|
152
152
|
Object.values(modelDefinition.fields)
|
|
153
153
|
.filter(({ association }) => association && Object.keys(association).length)
|
|
@@ -169,11 +169,14 @@ function getConnectionFields(
|
|
|
169
169
|
const connectedModelName =
|
|
170
170
|
modelDefinition.fields[name].type['model'];
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
const byPkIndex = relations[connectedModelName].indexes.find(
|
|
173
|
+
([name]) => name === 'byPk'
|
|
174
|
+
);
|
|
175
|
+
const keyFields = byPkIndex && byPkIndex[1];
|
|
176
|
+
const keyFieldSelectionSet = keyFields?.join(' ');
|
|
174
177
|
|
|
175
178
|
// We rely on `_deleted` when we process the sync query (e.g. in batchSave in the adapters)
|
|
176
|
-
result.push(`${name} { ${
|
|
179
|
+
result.push(`${name} { ${keyFieldSelectionSet} _deleted }`);
|
|
177
180
|
} else {
|
|
178
181
|
// backwards-compatability for schema generated prior to custom primary key support
|
|
179
182
|
result.push(`${name} { id _deleted }`);
|
package/src/types.ts
CHANGED
|
@@ -800,8 +800,17 @@ export type RelationType = {
|
|
|
800
800
|
associatedWith?: string | string[];
|
|
801
801
|
};
|
|
802
802
|
|
|
803
|
+
type IndexOptions = {
|
|
804
|
+
unique?: boolean;
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
export type IndexesType = Array<[string, string[], IndexOptions?]>;
|
|
808
|
+
|
|
803
809
|
export type RelationshipType = {
|
|
804
|
-
[modelName: string]: {
|
|
810
|
+
[modelName: string]: {
|
|
811
|
+
indexes: IndexesType;
|
|
812
|
+
relationTypes: RelationType[];
|
|
813
|
+
};
|
|
805
814
|
};
|
|
806
815
|
|
|
807
816
|
//#endregion
|
package/src/util.ts
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
AllOperators,
|
|
8
8
|
isPredicateGroup,
|
|
9
9
|
isPredicateObj,
|
|
10
|
-
ModelAssociation,
|
|
11
10
|
ModelInstanceMetadata,
|
|
12
11
|
PersistentModel,
|
|
13
12
|
PersistentModelConstructor,
|
|
@@ -29,10 +28,16 @@ import {
|
|
|
29
28
|
LimitTimerRaceResolvedValues,
|
|
30
29
|
SchemaModel,
|
|
31
30
|
ModelAttribute,
|
|
31
|
+
IndexesType,
|
|
32
32
|
} from './types';
|
|
33
33
|
import { WordArray } from 'amazon-cognito-identity-js';
|
|
34
|
+
|
|
34
35
|
const ID = 'id';
|
|
35
36
|
|
|
37
|
+
export const errorMessages = {
|
|
38
|
+
idEmptyString: 'An index field cannot contain an empty string value',
|
|
39
|
+
};
|
|
40
|
+
|
|
36
41
|
export function extractKeyIfExists(
|
|
37
42
|
modelDefinition: SchemaModel
|
|
38
43
|
): ModelAttribute | undefined {
|
|
@@ -59,6 +64,15 @@ export function extractPrimaryKeyValues<T extends PersistentModel>(
|
|
|
59
64
|
return keyFields.map(key => model[key]);
|
|
60
65
|
}
|
|
61
66
|
|
|
67
|
+
export function extractPrimaryKeysAndValues<T extends PersistentModel>(
|
|
68
|
+
model: T,
|
|
69
|
+
keyFields: string[]
|
|
70
|
+
): any {
|
|
71
|
+
const primaryKeysAndValues = {};
|
|
72
|
+
keyFields.forEach(key => (primaryKeysAndValues[key] = model[key]));
|
|
73
|
+
return primaryKeysAndValues;
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
// IdentifierFields<ManagedIdentifier>
|
|
63
77
|
// Default behavior without explicit @primaryKey defined
|
|
64
78
|
export function isIdManaged(modelDefinition: SchemaModel): boolean {
|
|
@@ -335,25 +349,13 @@ export const establishRelationAndKeys = (
|
|
|
335
349
|
});
|
|
336
350
|
|
|
337
351
|
if (connectionType === 'BELONGS_TO') {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (fieldAttribute.association.targetName) {
|
|
346
|
-
// backwards-compatability for schema generated prior to custom primary key support
|
|
347
|
-
relationship[mKey].indexes.push(
|
|
348
|
-
fieldAttribute.association['targetName']
|
|
349
|
-
);
|
|
350
|
-
} else {
|
|
351
|
-
// iterate through fieldAttribute.association.targetNames, and push to relationship[mKey].indexes
|
|
352
|
-
fieldAttribute.association.targetNames.forEach(
|
|
353
|
-
(targetName: string) => {
|
|
354
|
-
relationship[mKey].indexes.push(targetName);
|
|
355
|
-
}
|
|
356
|
-
);
|
|
352
|
+
const targetNames = extractTargetNamesFromSrc(
|
|
353
|
+
fieldAttribute.association
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
if (targetNames) {
|
|
357
|
+
const idxName = indexNameFromKeys(targetNames);
|
|
358
|
+
relationship[mKey].indexes.push([idxName, targetNames]);
|
|
357
359
|
}
|
|
358
360
|
}
|
|
359
361
|
}
|
|
@@ -367,24 +369,36 @@ export const establishRelationAndKeys = (
|
|
|
367
369
|
continue;
|
|
368
370
|
}
|
|
369
371
|
|
|
372
|
+
const { fields } = attribute.properties;
|
|
373
|
+
|
|
370
374
|
if (isModelAttributePrimaryKey(attribute)) {
|
|
371
|
-
keys[mKey].primaryKey =
|
|
375
|
+
keys[mKey].primaryKey = fields;
|
|
376
|
+
continue;
|
|
372
377
|
}
|
|
373
378
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
379
|
+
// create indexes for all other keys
|
|
380
|
+
const idxName = indexNameFromKeys(fields);
|
|
381
|
+
const idxExists = relationship[mKey].indexes.find(
|
|
382
|
+
([index]) => index === idxName
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
if (!idxExists) {
|
|
386
|
+
relationship[mKey].indexes.push([idxName, fields]);
|
|
381
387
|
}
|
|
382
388
|
}
|
|
389
|
+
}
|
|
383
390
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
391
|
+
// set 'id' as the PK for models without a custom PK explicitly defined
|
|
392
|
+
if (!keys[mKey].primaryKey) {
|
|
393
|
+
keys[mKey].primaryKey = [ID];
|
|
387
394
|
}
|
|
395
|
+
|
|
396
|
+
// create primary index
|
|
397
|
+
relationship[mKey].indexes.push([
|
|
398
|
+
'byPk',
|
|
399
|
+
keys[mKey].primaryKey as string[],
|
|
400
|
+
{ unique: true },
|
|
401
|
+
]);
|
|
388
402
|
});
|
|
389
403
|
|
|
390
404
|
return [relationship, keys];
|
|
@@ -572,7 +586,7 @@ export const traverseModel = <T extends PersistentModel>(
|
|
|
572
586
|
return result;
|
|
573
587
|
};
|
|
574
588
|
|
|
575
|
-
export const getIndex = (rel: RelationType[], src: any): string => {
|
|
589
|
+
export const getIndex = (rel: RelationType[], src: any): string | undefined => {
|
|
576
590
|
let index = '';
|
|
577
591
|
rel.some((relItem: RelationType) => {
|
|
578
592
|
if (relItem.modelName === src) {
|
|
@@ -582,9 +596,20 @@ export const getIndex = (rel: RelationType[], src: any): string => {
|
|
|
582
596
|
return index;
|
|
583
597
|
};
|
|
584
598
|
|
|
585
|
-
export const getIndexFromAssociation = (
|
|
586
|
-
|
|
587
|
-
|
|
599
|
+
export const getIndexFromAssociation = (
|
|
600
|
+
indexes: IndexesType,
|
|
601
|
+
src: string | string[]
|
|
602
|
+
): string | undefined => {
|
|
603
|
+
let indexName: string;
|
|
604
|
+
|
|
605
|
+
if (Array.isArray(src)) {
|
|
606
|
+
indexName = indexNameFromKeys(src);
|
|
607
|
+
} else {
|
|
608
|
+
indexName = src;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const associationIndex = indexes.find(([idxName]) => idxName === indexName);
|
|
612
|
+
return associationIndex && associationIndex[0];
|
|
588
613
|
};
|
|
589
614
|
|
|
590
615
|
let privateModeCheckResult;
|
|
@@ -950,3 +975,14 @@ export function extractTargetNamesFromSrc(src: any): string[] | undefined {
|
|
|
950
975
|
return undefined;
|
|
951
976
|
}
|
|
952
977
|
}
|
|
978
|
+
|
|
979
|
+
// Generates snake-cased index name from an aray of key field names
|
|
980
|
+
// E.g. for keys `[id, title]` => 'id-title'
|
|
981
|
+
export function indexNameFromKeys(keys: string[]): string {
|
|
982
|
+
return keys.reduce((prev: string, cur: string, idx: number) => {
|
|
983
|
+
if (idx === 0) {
|
|
984
|
+
return cur;
|
|
985
|
+
}
|
|
986
|
+
return `${prev}-${cur}`;
|
|
987
|
+
}, '');
|
|
988
|
+
}
|