@aws-amplify/datastore 4.0.12 → 4.0.13-push-notification-dryrun.43
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/lib/datastore/datastore.d.ts +29 -3
- package/lib/datastore/datastore.js +308 -147
- package/lib/datastore/datastore.js.map +1 -1
- package/lib/predicates/index.d.ts +77 -7
- package/lib/predicates/index.js +142 -122
- package/lib/predicates/index.js.map +1 -1
- package/lib/predicates/next.d.ts +51 -10
- package/lib/predicates/next.js +111 -91
- package/lib/predicates/next.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageAdapter.d.ts +28 -30
- package/lib/storage/adapter/AsyncStorageAdapter.js +135 -532
- package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.d.ts +28 -29
- package/lib/storage/adapter/IndexedDBAdapter.js +490 -885
- package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib/storage/adapter/StorageAdapterBase.d.ts +134 -0
- package/lib/storage/adapter/StorageAdapterBase.js +439 -0
- package/lib/storage/adapter/StorageAdapterBase.js.map +1 -0
- package/lib/storage/relationship.d.ts +9 -0
- package/lib/storage/relationship.js +9 -0
- package/lib/storage/relationship.js.map +1 -1
- package/lib/storage/storage.d.ts +1 -1
- package/lib/storage/storage.js +4 -3
- package/lib/storage/storage.js.map +1 -1
- package/lib/sync/index.d.ts +15 -1
- package/lib/sync/index.js +80 -13
- package/lib/sync/index.js.map +1 -1
- package/lib/sync/outbox.js +14 -7
- package/lib/sync/outbox.js.map +1 -1
- package/lib/sync/processors/mutation.d.ts +10 -1
- package/lib/sync/processors/mutation.js +33 -12
- package/lib/sync/processors/mutation.js.map +1 -1
- package/lib/sync/processors/subscription.d.ts +7 -1
- package/lib/sync/processors/subscription.js +196 -135
- package/lib/sync/processors/subscription.js.map +1 -1
- package/lib/sync/processors/sync.d.ts +1 -1
- package/lib/sync/processors/sync.js.map +1 -1
- package/lib/sync/utils.d.ts +66 -2
- package/lib/sync/utils.js +264 -16
- package/lib/sync/utils.js.map +1 -1
- package/lib/types.d.ts +9 -1
- package/lib/types.js.map +1 -1
- package/lib/util.d.ts +16 -0
- package/lib/util.js +31 -2
- package/lib/util.js.map +1 -1
- package/lib-esm/datastore/datastore.d.ts +29 -3
- package/lib-esm/datastore/datastore.js +310 -149
- package/lib-esm/datastore/datastore.js.map +1 -1
- package/lib-esm/predicates/index.d.ts +77 -7
- package/lib-esm/predicates/index.js +143 -123
- package/lib-esm/predicates/index.js.map +1 -1
- package/lib-esm/predicates/next.d.ts +51 -10
- package/lib-esm/predicates/next.js +111 -91
- package/lib-esm/predicates/next.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +28 -30
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js +138 -535
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +28 -29
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +489 -884
- package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/StorageAdapterBase.d.ts +134 -0
- package/lib-esm/storage/adapter/StorageAdapterBase.js +437 -0
- package/lib-esm/storage/adapter/StorageAdapterBase.js.map +1 -0
- package/lib-esm/storage/relationship.d.ts +9 -0
- package/lib-esm/storage/relationship.js +9 -0
- package/lib-esm/storage/relationship.js.map +1 -1
- package/lib-esm/storage/storage.d.ts +1 -1
- package/lib-esm/storage/storage.js +4 -3
- package/lib-esm/storage/storage.js.map +1 -1
- package/lib-esm/sync/index.d.ts +15 -1
- package/lib-esm/sync/index.js +82 -15
- package/lib-esm/sync/index.js.map +1 -1
- package/lib-esm/sync/outbox.js +14 -7
- package/lib-esm/sync/outbox.js.map +1 -1
- package/lib-esm/sync/processors/mutation.d.ts +10 -1
- package/lib-esm/sync/processors/mutation.js +33 -12
- package/lib-esm/sync/processors/mutation.js.map +1 -1
- package/lib-esm/sync/processors/subscription.d.ts +7 -1
- package/lib-esm/sync/processors/subscription.js +197 -136
- package/lib-esm/sync/processors/subscription.js.map +1 -1
- package/lib-esm/sync/processors/sync.d.ts +1 -1
- package/lib-esm/sync/processors/sync.js.map +1 -1
- package/lib-esm/sync/utils.d.ts +66 -2
- package/lib-esm/sync/utils.js +261 -18
- package/lib-esm/sync/utils.js.map +1 -1
- package/lib-esm/types.d.ts +9 -1
- package/lib-esm/types.js.map +1 -1
- package/lib-esm/util.d.ts +16 -0
- package/lib-esm/util.js +32 -3
- package/lib-esm/util.js.map +1 -1
- package/package.json +12 -11
- package/src/datastore/datastore.ts +288 -159
- package/src/predicates/index.ts +145 -175
- package/src/predicates/next.ts +114 -81
- package/src/storage/adapter/AsyncStorageAdapter.ts +97 -563
- package/src/storage/adapter/AsyncStorageDatabase.ts +2 -2
- package/src/storage/adapter/IndexedDBAdapter.ts +318 -770
- package/src/storage/adapter/StorageAdapterBase.ts +545 -0
- package/src/storage/relationship.ts +9 -0
- package/src/storage/storage.ts +12 -9
- package/src/sync/index.ts +108 -20
- package/src/sync/outbox.ts +17 -11
- package/src/sync/processors/mutation.ts +35 -4
- package/src/sync/processors/subscription.ts +124 -10
- package/src/sync/processors/sync.ts +4 -1
- package/src/sync/utils.ts +285 -15
- package/src/types.ts +15 -2
- package/src/util.ts +40 -1
- package/CHANGELOG.md +0 -904
package/src/sync/utils.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
PersistentModel,
|
|
21
21
|
PersistentModelConstructor,
|
|
22
22
|
PredicatesGroup,
|
|
23
|
+
PredicateObject,
|
|
23
24
|
RelationshipType,
|
|
24
25
|
SchemaModel,
|
|
25
26
|
SchemaNamespace,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
InternalSchema,
|
|
29
30
|
AuthModeStrategy,
|
|
30
31
|
ModelAttributes,
|
|
32
|
+
isPredicateGroup,
|
|
31
33
|
} from '../types';
|
|
32
34
|
import {
|
|
33
35
|
extractPrimaryKeyFieldNames,
|
|
@@ -247,6 +249,7 @@ export function getAuthorizationRules(
|
|
|
247
249
|
groupClaim = 'cognito:groups',
|
|
248
250
|
allow: authStrategy = 'iam',
|
|
249
251
|
groups = [],
|
|
252
|
+
groupsField = '',
|
|
250
253
|
} = rule;
|
|
251
254
|
|
|
252
255
|
const isReadAuthorized = operations.includes('read');
|
|
@@ -263,6 +266,7 @@ export function getAuthorizationRules(
|
|
|
263
266
|
groupClaim,
|
|
264
267
|
authStrategy,
|
|
265
268
|
groups,
|
|
269
|
+
groupsField,
|
|
266
270
|
areSubscriptionsPublic: false,
|
|
267
271
|
};
|
|
268
272
|
|
|
@@ -300,26 +304,36 @@ export function buildSubscriptionGraphQLOperation(
|
|
|
300
304
|
modelDefinition: SchemaModel,
|
|
301
305
|
transformerMutationType: TransformerMutationType,
|
|
302
306
|
isOwnerAuthorization: boolean,
|
|
303
|
-
ownerField: string
|
|
307
|
+
ownerField: string,
|
|
308
|
+
filterArg: boolean = false
|
|
304
309
|
): [TransformerMutationType, string, string] {
|
|
305
310
|
const selectionSet = generateSelectionSet(namespace, modelDefinition);
|
|
306
311
|
|
|
307
|
-
const { name: typeName
|
|
312
|
+
const { name: typeName } = modelDefinition;
|
|
308
313
|
|
|
309
314
|
const opName = `on${transformerMutationType}${typeName}`;
|
|
310
|
-
|
|
311
|
-
|
|
315
|
+
|
|
316
|
+
const docArgs: string[] = [];
|
|
317
|
+
const opArgs: string[] = [];
|
|
318
|
+
|
|
319
|
+
if (filterArg) {
|
|
320
|
+
docArgs.push(`$filter: ModelSubscription${typeName}FilterInput`);
|
|
321
|
+
opArgs.push('filter: $filter');
|
|
322
|
+
}
|
|
312
323
|
|
|
313
324
|
if (isOwnerAuthorization) {
|
|
314
|
-
docArgs
|
|
315
|
-
opArgs
|
|
325
|
+
docArgs.push(`$${ownerField}: String!`);
|
|
326
|
+
opArgs.push(`${ownerField}: $${ownerField}`);
|
|
316
327
|
}
|
|
317
328
|
|
|
329
|
+
const docStr = docArgs.length ? `(${docArgs.join(',')})` : '';
|
|
330
|
+
const opStr = opArgs.length ? `(${opArgs.join(',')})` : '';
|
|
331
|
+
|
|
318
332
|
return [
|
|
319
333
|
transformerMutationType,
|
|
320
334
|
opName,
|
|
321
|
-
`subscription operation${
|
|
322
|
-
${opName}${
|
|
335
|
+
`subscription operation${docStr}{
|
|
336
|
+
${opName}${opStr}{
|
|
323
337
|
${selectionSet}
|
|
324
338
|
}
|
|
325
339
|
}`,
|
|
@@ -476,10 +490,25 @@ export function predicateToGraphQLCondition(
|
|
|
476
490
|
const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
|
|
477
491
|
return predicateToGraphQLFilter(predicate, keyFields) as GraphQLCondition;
|
|
478
492
|
}
|
|
479
|
-
|
|
493
|
+
/**
|
|
494
|
+
* @param predicatesGroup - Predicate Group
|
|
495
|
+
@returns GQL Filter Expression from Predicate Group
|
|
496
|
+
|
|
497
|
+
@remarks Flattens redundant list predicates
|
|
498
|
+
@example
|
|
499
|
+
|
|
500
|
+
```js
|
|
501
|
+
{ and:[{ and:[{ username: { eq: 'bob' }}] }] }
|
|
502
|
+
```
|
|
503
|
+
Becomes
|
|
504
|
+
```js
|
|
505
|
+
{ and:[{ username: { eq: 'bob' }}] }
|
|
506
|
+
```
|
|
507
|
+
*/
|
|
480
508
|
export function predicateToGraphQLFilter(
|
|
481
509
|
predicatesGroup: PredicatesGroup<any>,
|
|
482
|
-
fieldsToOmit: string[] = []
|
|
510
|
+
fieldsToOmit: string[] = [],
|
|
511
|
+
root = true
|
|
483
512
|
): GraphQLFilter {
|
|
484
513
|
const result: GraphQLFilter = {};
|
|
485
514
|
|
|
@@ -492,8 +521,7 @@ export function predicateToGraphQLFilter(
|
|
|
492
521
|
|
|
493
522
|
result[type] = isList ? [] : {};
|
|
494
523
|
|
|
495
|
-
const
|
|
496
|
-
isList ? result[type].push(value) : (result[type] = value);
|
|
524
|
+
const children: GraphQLFilter[] = [];
|
|
497
525
|
|
|
498
526
|
predicates.forEach(predicate => {
|
|
499
527
|
if (isPredicateObj(predicate)) {
|
|
@@ -505,12 +533,37 @@ export function predicateToGraphQLFilter(
|
|
|
505
533
|
[field]: { [operator]: operand },
|
|
506
534
|
};
|
|
507
535
|
|
|
508
|
-
|
|
536
|
+
children.push(gqlField);
|
|
509
537
|
return;
|
|
510
538
|
}
|
|
511
539
|
|
|
512
|
-
const child = predicateToGraphQLFilter(predicate, fieldsToOmit);
|
|
513
|
-
Object.keys(child).length > 0
|
|
540
|
+
const child = predicateToGraphQLFilter(predicate, fieldsToOmit, false);
|
|
541
|
+
if (Object.keys(child).length > 0) {
|
|
542
|
+
children.push(child);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
// flatten redundant list predicates
|
|
547
|
+
if (children.length === 1) {
|
|
548
|
+
const [child] = children;
|
|
549
|
+
if (
|
|
550
|
+
// any nested list node
|
|
551
|
+
(isList && !root) ||
|
|
552
|
+
// root list node where the only child is also a list node
|
|
553
|
+
(isList && root && ('and' in child || 'or' in child))
|
|
554
|
+
) {
|
|
555
|
+
delete result[type];
|
|
556
|
+
Object.assign(result, child);
|
|
557
|
+
return result;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
children.forEach(child => {
|
|
562
|
+
if (isList) {
|
|
563
|
+
result[type].push(child);
|
|
564
|
+
} else {
|
|
565
|
+
result[type] = child;
|
|
566
|
+
}
|
|
514
567
|
});
|
|
515
568
|
|
|
516
569
|
if (isList) {
|
|
@@ -522,6 +575,223 @@ export function predicateToGraphQLFilter(
|
|
|
522
575
|
return result;
|
|
523
576
|
}
|
|
524
577
|
|
|
578
|
+
/**
|
|
579
|
+
*
|
|
580
|
+
* @param group - selective sync predicate group
|
|
581
|
+
* @returns set of distinct field names in the filter group
|
|
582
|
+
*/
|
|
583
|
+
export function filterFields(group?: PredicatesGroup<any>): Set<string> {
|
|
584
|
+
const fields = new Set<string>();
|
|
585
|
+
|
|
586
|
+
if (!group || !Array.isArray(group.predicates)) return fields;
|
|
587
|
+
|
|
588
|
+
const { predicates } = group;
|
|
589
|
+
const stack = [...predicates];
|
|
590
|
+
|
|
591
|
+
while (stack.length > 0) {
|
|
592
|
+
const current = stack.pop();
|
|
593
|
+
if (isPredicateObj(current)) {
|
|
594
|
+
fields.add(current.field as string);
|
|
595
|
+
} else if (isPredicateGroup(current)) {
|
|
596
|
+
stack.push(...current.predicates);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return fields;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
*
|
|
605
|
+
* @param modelDefinition
|
|
606
|
+
* @returns set of field names used with dynamic auth modes configured for the provided model definition
|
|
607
|
+
*/
|
|
608
|
+
export function dynamicAuthFields(modelDefinition: SchemaModel): Set<string> {
|
|
609
|
+
const rules = getAuthorizationRules(modelDefinition);
|
|
610
|
+
const fields = new Set<string>();
|
|
611
|
+
|
|
612
|
+
for (const rule of rules) {
|
|
613
|
+
if (rule.groupsField && !rule.groups.length) {
|
|
614
|
+
// dynamic group rule will have no values in `rule.groups`
|
|
615
|
+
fields.add((rule as AuthorizationRule).groupsField);
|
|
616
|
+
} else if (rule.ownerField) {
|
|
617
|
+
fields.add(rule.ownerField);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return fields;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
*
|
|
626
|
+
* @param group - selective sync predicate group
|
|
627
|
+
* @returns the total number of OR'd predicates in the filter group
|
|
628
|
+
*
|
|
629
|
+
* @example returns 2
|
|
630
|
+
* ```js
|
|
631
|
+
* { type: "or", predicates: [
|
|
632
|
+
* { field: "username", operator: "beginsWith", operand: "a" },
|
|
633
|
+
* { field: "title", operator: "contains", operand: "abc" },
|
|
634
|
+
* ]}
|
|
635
|
+
* ```
|
|
636
|
+
*/
|
|
637
|
+
export function countFilterCombinations(group?: PredicatesGroup<any>): number {
|
|
638
|
+
if (!group || !Array.isArray(group.predicates)) return 0;
|
|
639
|
+
|
|
640
|
+
let count = 0;
|
|
641
|
+
const stack: (PredicatesGroup<any> | PredicateObject<any>)[] = [group];
|
|
642
|
+
|
|
643
|
+
while (stack.length > 0) {
|
|
644
|
+
const current = stack.pop();
|
|
645
|
+
|
|
646
|
+
if (isPredicateGroup(current)) {
|
|
647
|
+
const { predicates, type } = current;
|
|
648
|
+
// ignore length = 1; groups with 1 predicate will get flattened when converted to gqlFilter
|
|
649
|
+
if (type === 'or' && predicates.length > 1) {
|
|
650
|
+
count += predicates.length;
|
|
651
|
+
}
|
|
652
|
+
stack.push(...predicates);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// if we didn't encounter any OR groups, default to 1
|
|
657
|
+
return count || 1;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
*
|
|
662
|
+
* @param group - selective sync predicate group
|
|
663
|
+
* @returns name of repeated field | null
|
|
664
|
+
*
|
|
665
|
+
* @example returns "username"
|
|
666
|
+
* ```js
|
|
667
|
+
* { type: "and", predicates: [
|
|
668
|
+
* { field: "username", operator: "beginsWith", operand: "a" },
|
|
669
|
+
* { field: "username", operator: "contains", operand: "abc" },
|
|
670
|
+
* ] }
|
|
671
|
+
* ```
|
|
672
|
+
*/
|
|
673
|
+
export function repeatedFieldInGroup(
|
|
674
|
+
group?: PredicatesGroup<any>
|
|
675
|
+
): string | null {
|
|
676
|
+
if (!group || !Array.isArray(group.predicates)) return null;
|
|
677
|
+
|
|
678
|
+
// convert to filter in order to flatten redundant groups
|
|
679
|
+
const gqlFilter = predicateToGraphQLFilter(group);
|
|
680
|
+
|
|
681
|
+
const stack: GraphQLFilter[] = [gqlFilter];
|
|
682
|
+
|
|
683
|
+
const hasGroupRepeatedFields = (fields: GraphQLFilter[]): string | null => {
|
|
684
|
+
const seen = {};
|
|
685
|
+
|
|
686
|
+
for (const f of fields) {
|
|
687
|
+
const [fieldName] = Object.keys(f);
|
|
688
|
+
if (seen[fieldName]) {
|
|
689
|
+
return fieldName;
|
|
690
|
+
}
|
|
691
|
+
seen[fieldName] = true;
|
|
692
|
+
}
|
|
693
|
+
return null;
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
while (stack.length > 0) {
|
|
697
|
+
const current = stack.pop();
|
|
698
|
+
|
|
699
|
+
const [key] = Object.keys(current!);
|
|
700
|
+
const values = current![key];
|
|
701
|
+
|
|
702
|
+
if (!Array.isArray(values)) {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// field value will be single object
|
|
707
|
+
const predicateObjects = values.filter(
|
|
708
|
+
v => !Array.isArray(Object.values(v)[0])
|
|
709
|
+
);
|
|
710
|
+
|
|
711
|
+
// group value will be an array
|
|
712
|
+
const predicateGroups = values.filter(v =>
|
|
713
|
+
Array.isArray(Object.values(v)[0])
|
|
714
|
+
);
|
|
715
|
+
|
|
716
|
+
if (key === 'and') {
|
|
717
|
+
const repeatedField = hasGroupRepeatedFields(predicateObjects);
|
|
718
|
+
if (repeatedField) {
|
|
719
|
+
return repeatedField;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
stack.push(...predicateGroups);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
export enum RTFError {
|
|
730
|
+
UnknownField,
|
|
731
|
+
MaxAttributes,
|
|
732
|
+
MaxCombinations,
|
|
733
|
+
RepeatedFieldname,
|
|
734
|
+
NotGroup,
|
|
735
|
+
FieldNotInType,
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
export function generateRTFRemediation(
|
|
739
|
+
errorType: RTFError,
|
|
740
|
+
modelDefinition: SchemaModel,
|
|
741
|
+
predicatesGroup: PredicatesGroup<any> | undefined
|
|
742
|
+
): string {
|
|
743
|
+
const selSyncFields = filterFields(predicatesGroup);
|
|
744
|
+
const selSyncFieldStr = [...selSyncFields].join(', ');
|
|
745
|
+
const dynamicAuthModeFields = dynamicAuthFields(modelDefinition);
|
|
746
|
+
const dynamicAuthFieldsStr = [...dynamicAuthModeFields].join(', ');
|
|
747
|
+
const filterCombinations = countFilterCombinations(predicatesGroup);
|
|
748
|
+
const repeatedField = repeatedFieldInGroup(predicatesGroup);
|
|
749
|
+
|
|
750
|
+
switch (errorType) {
|
|
751
|
+
case RTFError.UnknownField:
|
|
752
|
+
return (
|
|
753
|
+
`Your API was generated with an older version of the CLI that doesn't support backend subscription filtering.` +
|
|
754
|
+
'To enable backend subscription filtering, upgrade your Amplify CLI to the latest version and push your app by running `amplify upgrade` followed by `amplify push`'
|
|
755
|
+
);
|
|
756
|
+
|
|
757
|
+
case RTFError.MaxAttributes: {
|
|
758
|
+
let message = `Your selective sync expression for ${modelDefinition.name} contains ${selSyncFields.size} different model fields: ${selSyncFieldStr}.\n\n`;
|
|
759
|
+
|
|
760
|
+
if (dynamicAuthModeFields.size > 0) {
|
|
761
|
+
message +=
|
|
762
|
+
`Note: the number of fields you can use with selective sync is affected by @auth rules configured on the model.\n\n` +
|
|
763
|
+
`Dynamic auth modes, such as owner auth and dynamic group auth each utilize 1 field.\n` +
|
|
764
|
+
`You currently have ${dynamicAuthModeFields.size} dynamic auth mode(s) configured on this model: ${dynamicAuthFieldsStr}.`;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return message;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
case RTFError.MaxCombinations: {
|
|
771
|
+
let message = `Your selective sync expression for ${modelDefinition.name} contains ${filterCombinations} field combinations (total number of predicates in an OR expression).\n\n`;
|
|
772
|
+
|
|
773
|
+
if (dynamicAuthModeFields.size > 0) {
|
|
774
|
+
message +=
|
|
775
|
+
`Note: the number of fields you can use with selective sync is affected by @auth rules configured on the model.\n\n` +
|
|
776
|
+
`Dynamic auth modes, such as owner auth and dynamic group auth factor in to the number of combinations you're using.\n` +
|
|
777
|
+
`You currently have ${dynamicAuthModeFields.size} dynamic auth mode(s) configured on this model: ${dynamicAuthFieldsStr}.`;
|
|
778
|
+
}
|
|
779
|
+
return message;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
case RTFError.RepeatedFieldname:
|
|
783
|
+
return `Your selective sync expression for ${modelDefinition.name} contains multiple entries for ${repeatedField} in the same AND group.`;
|
|
784
|
+
case RTFError.NotGroup:
|
|
785
|
+
return (
|
|
786
|
+
`Your selective sync expression for ${modelDefinition.name} uses a \`not\` group. If you'd like to filter subscriptions in the backend, ` +
|
|
787
|
+
`rewrite your expression using \`ne\` or \`notContains\` operators.`
|
|
788
|
+
);
|
|
789
|
+
case RTFError.FieldNotInType:
|
|
790
|
+
// no remediation instructions. We'll surface the message directly
|
|
791
|
+
return '';
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
525
795
|
export function getUserGroupsFromToken(
|
|
526
796
|
token: { [field: string]: any },
|
|
527
797
|
rule: AuthorizationRule
|
package/src/types.ts
CHANGED
|
@@ -48,7 +48,17 @@ export type SchemaModel = {
|
|
|
48
48
|
name: string;
|
|
49
49
|
pluralName: string;
|
|
50
50
|
attributes?: ModelAttributes;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Explicitly defined fields.
|
|
54
|
+
*/
|
|
51
55
|
fields: ModelFields;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Explicitly defined fields plus implied fields. (E.g., foreign keys.)
|
|
59
|
+
*/
|
|
60
|
+
allFields?: ModelFields;
|
|
61
|
+
|
|
52
62
|
syncable?: boolean;
|
|
53
63
|
};
|
|
54
64
|
|
|
@@ -306,6 +316,7 @@ export type AuthorizationRule = {
|
|
|
306
316
|
provider: 'userPools' | 'oidc' | 'iam' | 'apiKey';
|
|
307
317
|
groupClaim: string;
|
|
308
318
|
groups: [string];
|
|
319
|
+
groupsField: string;
|
|
309
320
|
authStrategy: 'owner' | 'groups' | 'private' | 'public';
|
|
310
321
|
areSubscriptionsPublic: boolean;
|
|
311
322
|
};
|
|
@@ -587,9 +598,11 @@ type DeepWritable<T> = {
|
|
|
587
598
|
-readonly [P in keyof T]: T[P] extends TypeName<T[P]>
|
|
588
599
|
? T[P]
|
|
589
600
|
: T[P] extends Promise<infer InnerPromiseType>
|
|
590
|
-
? InnerPromiseType
|
|
601
|
+
? undefined extends InnerPromiseType
|
|
602
|
+
? InnerPromiseType | null
|
|
603
|
+
: InnerPromiseType
|
|
591
604
|
: T[P] extends AsyncCollection<infer InnerCollectionType>
|
|
592
|
-
? InnerCollectionType[] | undefined
|
|
605
|
+
? InnerCollectionType[] | undefined | null
|
|
593
606
|
: DeepWritable<T[P]>;
|
|
594
607
|
};
|
|
595
608
|
|
package/src/util.ts
CHANGED
|
@@ -962,7 +962,13 @@ export const establishRelationAndKeys = (
|
|
|
962
962
|
|
|
963
963
|
if (targetNames) {
|
|
964
964
|
const idxName = indexNameFromKeys(targetNames);
|
|
965
|
-
relationship[mKey].indexes.
|
|
965
|
+
const idxExists = relationship[mKey].indexes.find(
|
|
966
|
+
([index]) => index === idxName
|
|
967
|
+
);
|
|
968
|
+
|
|
969
|
+
if (!idxExists) {
|
|
970
|
+
relationship[mKey].indexes.push([idxName, targetNames]);
|
|
971
|
+
}
|
|
966
972
|
}
|
|
967
973
|
}
|
|
968
974
|
}
|
|
@@ -1098,3 +1104,36 @@ export const getIndexKeys = (
|
|
|
1098
1104
|
};
|
|
1099
1105
|
|
|
1100
1106
|
//#endregion
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* Determine what the managed timestamp field names are for the given model definition
|
|
1110
|
+
* and return the mapping.
|
|
1111
|
+
*
|
|
1112
|
+
* All timestamp fields are included in the mapping, regardless of whether the final field
|
|
1113
|
+
* names are the defaults or customized in the `@model` directive.
|
|
1114
|
+
*
|
|
1115
|
+
* @see https://docs.amplify.aws/cli/graphql/data-modeling/#customize-creation-and-update-timestamps
|
|
1116
|
+
*
|
|
1117
|
+
* @param definition modelDefinition to inspect.
|
|
1118
|
+
* @returns An object mapping `createdAt` and `updatedAt` to their field names.
|
|
1119
|
+
*/
|
|
1120
|
+
export const getTimestampFields = (
|
|
1121
|
+
definition: SchemaModel
|
|
1122
|
+
): { createdAt: string; updatedAt: string } => {
|
|
1123
|
+
const modelAttributes = definition.attributes?.find(
|
|
1124
|
+
attr => attr.type === 'model'
|
|
1125
|
+
);
|
|
1126
|
+
const timestampFieldsMap = modelAttributes?.properties?.timestamps;
|
|
1127
|
+
|
|
1128
|
+
const defaultFields = {
|
|
1129
|
+
createdAt: 'createdAt',
|
|
1130
|
+
updatedAt: 'updatedAt',
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
const customFields = timestampFieldsMap || {};
|
|
1134
|
+
|
|
1135
|
+
return {
|
|
1136
|
+
...defaultFields,
|
|
1137
|
+
...customFields,
|
|
1138
|
+
};
|
|
1139
|
+
};
|