@aws-amplify/data-schema 1.1.1 → 1.1.2
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.
|
@@ -161,29 +161,48 @@ function transformFunctionHandler(handlers, functionFieldName) {
|
|
|
161
161
|
function customOperationToGql(typeName, typeDef, authorization, isCustom = false, databaseType, getRefType) {
|
|
162
162
|
const { arguments: fieldArgs, typeName: opType, returnType, handlers, subscriptionSource, } = typeDef.data;
|
|
163
163
|
let callSignature = typeName;
|
|
164
|
-
const
|
|
164
|
+
const implicitTypes = [];
|
|
165
|
+
// When Custom Operations are defined with a Custom Type return type,
|
|
166
|
+
// the Custom Type inherits the operation's auth rules
|
|
167
|
+
let customTypeAuthRules = undefined;
|
|
165
168
|
const { authString } = isCustom
|
|
166
|
-
?
|
|
169
|
+
? mapToNativeAppSyncAuthDirectives(authorization, true)
|
|
167
170
|
: calculateAuth(authorization);
|
|
168
171
|
/**
|
|
169
172
|
*
|
|
170
173
|
* @param returnType The return type from the `data` field of a customer operation.
|
|
171
174
|
* @param refererTypeName The type the refers {@link returnType} by `a.ref()`.
|
|
172
|
-
* @param
|
|
173
|
-
* the return type resolved CustomType to the `
|
|
175
|
+
* @param shouldAddCustomTypeToImplicitTypes A flag indicates wether it should push
|
|
176
|
+
* the return type resolved CustomType to the `implicitTypes` list.
|
|
174
177
|
* @returns
|
|
175
178
|
*/
|
|
176
|
-
const resolveReturnTypeNameFromReturnType = (returnType, { refererTypeName,
|
|
179
|
+
const resolveReturnTypeNameFromReturnType = (returnType, { refererTypeName, shouldAddCustomTypeToImplicitTypes = true, }) => {
|
|
177
180
|
if (isRefField(returnType)) {
|
|
181
|
+
const { type } = getRefType(returnType.data.link, typeName);
|
|
182
|
+
if (type === 'CustomType') {
|
|
183
|
+
customTypeAuthRules = {
|
|
184
|
+
typeName: returnType.data.link,
|
|
185
|
+
authRules: authorization,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
178
188
|
return refFieldToGql(returnType?.data);
|
|
179
189
|
}
|
|
180
190
|
else if (isCustomType(returnType)) {
|
|
181
191
|
const returnTypeName = `${capitalize(refererTypeName)}ReturnType`;
|
|
182
|
-
if (
|
|
183
|
-
|
|
192
|
+
if (shouldAddCustomTypeToImplicitTypes) {
|
|
193
|
+
customTypeAuthRules = {
|
|
194
|
+
typeName: returnTypeName,
|
|
195
|
+
authRules: authorization,
|
|
196
|
+
};
|
|
197
|
+
implicitTypes.push([returnTypeName, returnType]);
|
|
184
198
|
}
|
|
185
199
|
return returnTypeName;
|
|
186
200
|
}
|
|
201
|
+
else if (isEnumType(returnType)) {
|
|
202
|
+
const returnTypeName = `${capitalize(refererTypeName)}ReturnType`;
|
|
203
|
+
implicitTypes.push([returnTypeName, returnType]);
|
|
204
|
+
return returnTypeName;
|
|
205
|
+
}
|
|
187
206
|
else if (isScalarField(returnType)) {
|
|
188
207
|
return scalarFieldToGql(returnType?.data);
|
|
189
208
|
}
|
|
@@ -199,7 +218,7 @@ function customOperationToGql(typeName, typeDef, authorization, isCustom = false
|
|
|
199
218
|
if (type === 'CustomOperation') {
|
|
200
219
|
returnTypeName = resolveReturnTypeNameFromReturnType(def.data.returnType, {
|
|
201
220
|
refererTypeName: subscriptionSource[0].data.link,
|
|
202
|
-
|
|
221
|
+
shouldAddCustomTypeToImplicitTypes: false,
|
|
203
222
|
});
|
|
204
223
|
}
|
|
205
224
|
else {
|
|
@@ -212,9 +231,9 @@ function customOperationToGql(typeName, typeDef, authorization, isCustom = false
|
|
|
212
231
|
});
|
|
213
232
|
}
|
|
214
233
|
if (Object.keys(fieldArgs).length > 0) {
|
|
215
|
-
const { gqlFields,
|
|
234
|
+
const { gqlFields, implicitTypes } = processFields(typeName, fieldArgs, {}, {});
|
|
216
235
|
callSignature += `(${gqlFields.join(', ')})`;
|
|
217
|
-
|
|
236
|
+
implicitTypes.push(...implicitTypes);
|
|
218
237
|
}
|
|
219
238
|
const handler = handlers && handlers[0];
|
|
220
239
|
const brand = handler && (0, util_1.getBrand)(handler);
|
|
@@ -262,7 +281,8 @@ function customOperationToGql(typeName, typeDef, authorization, isCustom = false
|
|
|
262
281
|
const gqlField = `${callSignature}: ${returnTypeName} ${gqlHandlerContent}${authString}`;
|
|
263
282
|
return {
|
|
264
283
|
gqlField,
|
|
265
|
-
|
|
284
|
+
implicitTypes: implicitTypes,
|
|
285
|
+
customTypeAuthRules,
|
|
266
286
|
lambdaFunctionDefinition,
|
|
267
287
|
customSqlDataSourceStrategy,
|
|
268
288
|
};
|
|
@@ -443,23 +463,29 @@ function calculateAuth(authorization) {
|
|
|
443
463
|
const authString = rules.length > 0 ? `@auth(rules: [${rules.join(',\n ')}])` : '';
|
|
444
464
|
return { authString, authFields };
|
|
445
465
|
}
|
|
446
|
-
function
|
|
466
|
+
function validateCustomHandlerAuthRule(rule) {
|
|
447
467
|
if (rule.groups && rule.provider === 'oidc') {
|
|
448
468
|
throw new Error('OIDC group auth is not supported with a.handler.custom');
|
|
449
469
|
}
|
|
470
|
+
// not currently supported with handler.custom (JS Resolvers), but will be in the future
|
|
471
|
+
if (rule.provider === 'identityPool' || rule.provider === 'iam') {
|
|
472
|
+
throw new Error("identityPool-based auth (allow.guest() and allow.authenticated('identityPool')) is not supported with a.handler.custom");
|
|
473
|
+
}
|
|
450
474
|
}
|
|
451
|
-
function
|
|
475
|
+
function getAppSyncAuthDirectiveFromRule(rule) {
|
|
452
476
|
const strategyDict = {
|
|
453
477
|
public: {
|
|
454
478
|
default: '@aws_api_key',
|
|
455
479
|
apiKey: '@aws_api_key',
|
|
456
480
|
iam: '@aws_iam',
|
|
481
|
+
identityPool: '@aws_iam',
|
|
457
482
|
},
|
|
458
483
|
private: {
|
|
459
484
|
default: '@aws_cognito_user_pools',
|
|
460
485
|
userPools: '@aws_cognito_user_pools',
|
|
461
486
|
oidc: '@aws_oidc',
|
|
462
487
|
iam: '@aws_iam',
|
|
488
|
+
identityPool: '@aws_iam',
|
|
463
489
|
},
|
|
464
490
|
groups: {
|
|
465
491
|
default: '@aws_cognito_user_pools',
|
|
@@ -481,23 +507,23 @@ function getCustomAuthProvider(rule) {
|
|
|
481
507
|
}
|
|
482
508
|
return stratProvider;
|
|
483
509
|
}
|
|
484
|
-
function
|
|
485
|
-
const rules =
|
|
510
|
+
function mapToNativeAppSyncAuthDirectives(authorization, isCustomHandler) {
|
|
511
|
+
const rules = new Set();
|
|
486
512
|
for (const entry of authorization) {
|
|
487
513
|
const rule = (0, Authorization_1.accessData)(entry);
|
|
488
|
-
|
|
489
|
-
const provider =
|
|
514
|
+
isCustomHandler && validateCustomHandlerAuthRule(rule);
|
|
515
|
+
const provider = getAppSyncAuthDirectiveFromRule(rule);
|
|
490
516
|
if (rule.groups) {
|
|
491
517
|
// example: (cognito_groups: ["Bloggers", "Readers"])
|
|
492
|
-
rules.
|
|
518
|
+
rules.add(`${provider}(cognito_groups: [${rule.groups
|
|
493
519
|
.map((group) => `"${group}"`)
|
|
494
520
|
.join(', ')}])`);
|
|
495
521
|
}
|
|
496
522
|
else {
|
|
497
|
-
rules.
|
|
523
|
+
rules.add(provider);
|
|
498
524
|
}
|
|
499
525
|
}
|
|
500
|
-
const authString = rules.join(' ');
|
|
526
|
+
const authString = [...rules].join(' ');
|
|
501
527
|
return { authString };
|
|
502
528
|
}
|
|
503
529
|
function capitalize(s) {
|
|
@@ -518,7 +544,9 @@ function processFieldLevelAuthRules(fields, authFields) {
|
|
|
518
544
|
}
|
|
519
545
|
function processFields(typeName, fields, impliedFields, fieldLevelAuthRules, identifier, partitionKey, secondaryIndexes = {}) {
|
|
520
546
|
const gqlFields = [];
|
|
521
|
-
|
|
547
|
+
// stores nested, field-level type definitions (custom types and enums)
|
|
548
|
+
// the need to be hoisted to top-level schema types and processed accordingly
|
|
549
|
+
const implicitTypes = [];
|
|
522
550
|
validateImpliedFields(fields, impliedFields);
|
|
523
551
|
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
524
552
|
const fieldAuth = fieldLevelAuthRules[fieldName]
|
|
@@ -538,14 +566,14 @@ function processFields(typeName, fields, impliedFields, fieldLevelAuthRules, ide
|
|
|
538
566
|
// The inline enum type name should be `<TypeName><FieldName>` to avoid
|
|
539
567
|
// enum type name conflicts
|
|
540
568
|
const enumName = `${capitalize(typeName)}${capitalize(fieldName)}`;
|
|
541
|
-
|
|
569
|
+
implicitTypes.push([enumName, fieldDef]);
|
|
542
570
|
gqlFields.push(`${fieldName}: ${enumFieldToGql(enumName, secondaryIndexes[fieldName])}`);
|
|
543
571
|
}
|
|
544
572
|
else if (isCustomType(fieldDef)) {
|
|
545
573
|
// The inline CustomType name should be `<TypeName><FieldName>` to avoid
|
|
546
574
|
// CustomType name conflicts
|
|
547
575
|
const customTypeName = `${capitalize(typeName)}${capitalize(fieldName)}`;
|
|
548
|
-
|
|
576
|
+
implicitTypes.push([customTypeName, fieldDef]);
|
|
549
577
|
gqlFields.push(`${fieldName}: ${customTypeName}`);
|
|
550
578
|
}
|
|
551
579
|
else {
|
|
@@ -556,7 +584,7 @@ function processFields(typeName, fields, impliedFields, fieldLevelAuthRules, ide
|
|
|
556
584
|
throw new Error(`Unexpected field definition: ${fieldDef}`);
|
|
557
585
|
}
|
|
558
586
|
}
|
|
559
|
-
return { gqlFields,
|
|
587
|
+
return { gqlFields, implicitTypes };
|
|
560
588
|
}
|
|
561
589
|
/**
|
|
562
590
|
*
|
|
@@ -665,11 +693,47 @@ const getRefTypeForSchema = (schema) => {
|
|
|
665
693
|
};
|
|
666
694
|
return getRefType;
|
|
667
695
|
};
|
|
696
|
+
/**
|
|
697
|
+
* Sorts top-level schema types to where Custom Types are processed last
|
|
698
|
+
* This allows us to accrue and then apply inherited auth rules for custom types from custom operations
|
|
699
|
+
* that reference them in their return values
|
|
700
|
+
*/
|
|
701
|
+
const sortTopLevelTypes = (topLevelTypes) => {
|
|
702
|
+
return topLevelTypes.sort(([_typeNameA, typeDefA], [_typeNameB, typeDefB]) => {
|
|
703
|
+
if ((isCustomType(typeDefA) && isCustomType(typeDefB)) ||
|
|
704
|
+
(!isCustomType(typeDefA) && !isCustomType(typeDefB))) {
|
|
705
|
+
return 0;
|
|
706
|
+
}
|
|
707
|
+
else if (isCustomType(typeDefA) && !isCustomType(typeDefB)) {
|
|
708
|
+
return 1;
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
return -1;
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
};
|
|
715
|
+
/**
|
|
716
|
+
* Builds up dictionary of Custom Type name - array of inherited auth rules
|
|
717
|
+
*/
|
|
718
|
+
const mergeCustomTypeAuthRules = (existing, added) => {
|
|
719
|
+
if (!added)
|
|
720
|
+
return;
|
|
721
|
+
const { typeName, authRules } = added;
|
|
722
|
+
if (typeName in existing) {
|
|
723
|
+
existing[typeName] = [...existing[typeName], ...authRules];
|
|
724
|
+
}
|
|
725
|
+
else {
|
|
726
|
+
existing[typeName] = authRules;
|
|
727
|
+
}
|
|
728
|
+
};
|
|
668
729
|
const schemaPreprocessor = (schema) => {
|
|
669
730
|
const gqlModels = [];
|
|
670
731
|
const customQueries = [];
|
|
671
732
|
const customMutations = [];
|
|
672
733
|
const customSubscriptions = [];
|
|
734
|
+
// Dict of auth rules to be applied to custom types
|
|
735
|
+
// Inherited from the auth configured on the custom operations that return these custom types
|
|
736
|
+
const customTypeInheritedAuthRules = {};
|
|
673
737
|
const jsFunctions = [];
|
|
674
738
|
const lambdaFunctions = {};
|
|
675
739
|
const customSqlDataSourceStrategies = [];
|
|
@@ -677,7 +741,7 @@ const schemaPreprocessor = (schema) => {
|
|
|
677
741
|
? 'dynamodb'
|
|
678
742
|
: 'sql';
|
|
679
743
|
const staticSchema = schema.data.configuration.database.engine === 'dynamodb' ? false : true;
|
|
680
|
-
const topLevelTypes = Object.entries(schema.data.types);
|
|
744
|
+
const topLevelTypes = sortTopLevelTypes(Object.entries(schema.data.types));
|
|
681
745
|
const { schemaAuth, functionSchemaAccess } = extractFunctionSchemaAccess(schema.data.authorization);
|
|
682
746
|
const getRefType = getRefTypeForSchema(schema);
|
|
683
747
|
for (const [typeName, typeDef] of topLevelTypes) {
|
|
@@ -696,20 +760,25 @@ const schemaPreprocessor = (schema) => {
|
|
|
696
760
|
const fields = typeDef.data.fields;
|
|
697
761
|
validateRefUseCases(typeName, 'customType', fields, getRefType);
|
|
698
762
|
const fieldAuthApplicableFields = Object.fromEntries(Object.entries(fields).filter((pair) => isModelField(pair[1])));
|
|
699
|
-
|
|
763
|
+
let customAuth = '';
|
|
764
|
+
if (typeName in customTypeInheritedAuthRules) {
|
|
765
|
+
const { authString } = mapToNativeAppSyncAuthDirectives(customTypeInheritedAuthRules[typeName], false);
|
|
766
|
+
customAuth = authString;
|
|
767
|
+
}
|
|
700
768
|
const authFields = {};
|
|
701
769
|
const fieldLevelAuthRules = processFieldLevelAuthRules(fieldAuthApplicableFields, authFields);
|
|
702
|
-
const { gqlFields,
|
|
703
|
-
topLevelTypes.push(...
|
|
770
|
+
const { gqlFields, implicitTypes } = processFields(typeName, fields, authFields, fieldLevelAuthRules);
|
|
771
|
+
topLevelTypes.push(...implicitTypes);
|
|
704
772
|
const joined = gqlFields.join('\n ');
|
|
705
|
-
const model = `type ${typeName} ${
|
|
773
|
+
const model = `type ${typeName} ${customAuth}\n{\n ${joined}\n}`;
|
|
706
774
|
gqlModels.push(model);
|
|
707
775
|
}
|
|
708
776
|
else if (isCustomOperation(typeDef)) {
|
|
709
777
|
const { typeName: opType } = typeDef.data;
|
|
710
|
-
const { gqlField,
|
|
778
|
+
const { gqlField, implicitTypes, customTypeAuthRules, jsFunctionForField, lambdaFunctionDefinition, customSqlDataSourceStrategy, } = transformCustomOperations(typeDef, typeName, mostRelevantAuthRules, databaseType, getRefType);
|
|
779
|
+
mergeCustomTypeAuthRules(customTypeInheritedAuthRules, customTypeAuthRules);
|
|
711
780
|
Object.assign(lambdaFunctions, lambdaFunctionDefinition);
|
|
712
|
-
topLevelTypes.push(...
|
|
781
|
+
topLevelTypes.push(...implicitTypes);
|
|
713
782
|
if (jsFunctionForField) {
|
|
714
783
|
jsFunctions.push(jsFunctionForField);
|
|
715
784
|
}
|
|
@@ -740,8 +809,8 @@ const schemaPreprocessor = (schema) => {
|
|
|
740
809
|
}
|
|
741
810
|
const fieldLevelAuthRules = processFieldLevelAuthRules(fields, authFields);
|
|
742
811
|
validateStaticFields(fields, authFields);
|
|
743
|
-
const { gqlFields,
|
|
744
|
-
topLevelTypes.push(...
|
|
812
|
+
const { gqlFields, implicitTypes } = processFields(typeName, fields, authFields, fieldLevelAuthRules, identifier, partitionKey);
|
|
813
|
+
topLevelTypes.push(...implicitTypes);
|
|
745
814
|
const joined = gqlFields.join('\n ');
|
|
746
815
|
// TODO: update @model(timestamps: null) once a longer term solution gets
|
|
747
816
|
// determined.
|
|
@@ -763,8 +832,8 @@ const schemaPreprocessor = (schema) => {
|
|
|
763
832
|
throw new Error(`Model \`${typeName}\` is missing authorization rules. Add global rules to the schema or ensure every model has its own rules.`);
|
|
764
833
|
}
|
|
765
834
|
const fieldLevelAuthRules = processFieldLevelAuthRules(fields, authFields);
|
|
766
|
-
const { gqlFields,
|
|
767
|
-
topLevelTypes.push(...
|
|
835
|
+
const { gqlFields, implicitTypes } = processFields(typeName, fields, authFields, fieldLevelAuthRules, identifier, partitionKey, transformedSecondaryIndexes);
|
|
836
|
+
topLevelTypes.push(...implicitTypes);
|
|
768
837
|
const joined = gqlFields.join('\n ');
|
|
769
838
|
const model = `type ${typeName} @model ${authString}\n{\n ${joined}\n}`;
|
|
770
839
|
gqlModels.push(model);
|
|
@@ -923,10 +992,11 @@ function transformCustomOperations(typeDef, typeName, authRules, databaseType, g
|
|
|
923
992
|
jsFunctionForField = handleCustom(handlers, opType, typeName);
|
|
924
993
|
}
|
|
925
994
|
const isCustom = Boolean(jsFunctionForField);
|
|
926
|
-
const { gqlField,
|
|
995
|
+
const { gqlField, implicitTypes, customTypeAuthRules, lambdaFunctionDefinition, customSqlDataSourceStrategy, } = customOperationToGql(typeName, typeDef, authRules, isCustom, databaseType, getRefType);
|
|
927
996
|
return {
|
|
928
997
|
gqlField,
|
|
929
|
-
|
|
998
|
+
implicitTypes,
|
|
999
|
+
customTypeAuthRules,
|
|
930
1000
|
jsFunctionForField,
|
|
931
1001
|
lambdaFunctionDefinition,
|
|
932
1002
|
customSqlDataSourceStrategy,
|