@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.
package/src/SchemaProcessor.ts
CHANGED
|
@@ -287,6 +287,13 @@ function transformFunctionHandler(
|
|
|
287
287
|
return { gqlHandlerContent, lambdaFunctionDefinition };
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
type CustomTypeAuthRules =
|
|
291
|
+
| {
|
|
292
|
+
typeName: string;
|
|
293
|
+
authRules: Authorization<any, any, any>[];
|
|
294
|
+
}
|
|
295
|
+
| undefined;
|
|
296
|
+
|
|
290
297
|
function customOperationToGql(
|
|
291
298
|
typeName: string,
|
|
292
299
|
typeDef: InternalCustom,
|
|
@@ -296,7 +303,8 @@ function customOperationToGql(
|
|
|
296
303
|
getRefType: ReturnType<typeof getRefTypeForSchema>,
|
|
297
304
|
): {
|
|
298
305
|
gqlField: string;
|
|
299
|
-
|
|
306
|
+
implicitTypes: [string, any][];
|
|
307
|
+
customTypeAuthRules: CustomTypeAuthRules;
|
|
300
308
|
lambdaFunctionDefinition: LambdaFunctionDefinition;
|
|
301
309
|
customSqlDataSourceStrategy: CustomSqlDataSourceStrategy | undefined;
|
|
302
310
|
} {
|
|
@@ -309,37 +317,60 @@ function customOperationToGql(
|
|
|
309
317
|
} = typeDef.data;
|
|
310
318
|
|
|
311
319
|
let callSignature: string = typeName;
|
|
312
|
-
const
|
|
320
|
+
const implicitTypes: [string, any][] = [];
|
|
321
|
+
|
|
322
|
+
// When Custom Operations are defined with a Custom Type return type,
|
|
323
|
+
// the Custom Type inherits the operation's auth rules
|
|
324
|
+
let customTypeAuthRules: CustomTypeAuthRules = undefined;
|
|
313
325
|
|
|
314
326
|
const { authString } = isCustom
|
|
315
|
-
?
|
|
327
|
+
? mapToNativeAppSyncAuthDirectives(authorization, true)
|
|
316
328
|
: calculateAuth(authorization);
|
|
317
329
|
|
|
318
330
|
/**
|
|
319
331
|
*
|
|
320
332
|
* @param returnType The return type from the `data` field of a customer operation.
|
|
321
333
|
* @param refererTypeName The type the refers {@link returnType} by `a.ref()`.
|
|
322
|
-
* @param
|
|
323
|
-
* the return type resolved CustomType to the `
|
|
334
|
+
* @param shouldAddCustomTypeToImplicitTypes A flag indicates wether it should push
|
|
335
|
+
* the return type resolved CustomType to the `implicitTypes` list.
|
|
324
336
|
* @returns
|
|
325
337
|
*/
|
|
326
338
|
const resolveReturnTypeNameFromReturnType = (
|
|
327
339
|
returnType: any,
|
|
328
340
|
{
|
|
329
341
|
refererTypeName,
|
|
330
|
-
|
|
342
|
+
shouldAddCustomTypeToImplicitTypes = true,
|
|
331
343
|
}: {
|
|
332
344
|
refererTypeName: string;
|
|
333
|
-
|
|
345
|
+
shouldAddCustomTypeToImplicitTypes?: boolean;
|
|
334
346
|
},
|
|
335
347
|
): string => {
|
|
336
348
|
if (isRefField(returnType)) {
|
|
349
|
+
const { type } = getRefType(returnType.data.link, typeName);
|
|
350
|
+
|
|
351
|
+
if (type === 'CustomType') {
|
|
352
|
+
customTypeAuthRules = {
|
|
353
|
+
typeName: returnType.data.link,
|
|
354
|
+
authRules: authorization,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
337
358
|
return refFieldToGql(returnType?.data);
|
|
338
359
|
} else if (isCustomType(returnType)) {
|
|
339
360
|
const returnTypeName = `${capitalize(refererTypeName)}ReturnType`;
|
|
340
|
-
if (
|
|
341
|
-
|
|
361
|
+
if (shouldAddCustomTypeToImplicitTypes) {
|
|
362
|
+
customTypeAuthRules = {
|
|
363
|
+
typeName: returnTypeName,
|
|
364
|
+
authRules: authorization,
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
implicitTypes.push([returnTypeName, returnType]);
|
|
342
368
|
}
|
|
369
|
+
return returnTypeName;
|
|
370
|
+
} else if (isEnumType(returnType)) {
|
|
371
|
+
const returnTypeName = `${capitalize(refererTypeName)}ReturnType`;
|
|
372
|
+
implicitTypes.push([returnTypeName, returnType]);
|
|
373
|
+
|
|
343
374
|
return returnTypeName;
|
|
344
375
|
} else if (isScalarField(returnType)) {
|
|
345
376
|
return scalarFieldToGql(returnType?.data);
|
|
@@ -359,7 +390,7 @@ function customOperationToGql(
|
|
|
359
390
|
def.data.returnType,
|
|
360
391
|
{
|
|
361
392
|
refererTypeName: subscriptionSource[0].data.link,
|
|
362
|
-
|
|
393
|
+
shouldAddCustomTypeToImplicitTypes: false,
|
|
363
394
|
},
|
|
364
395
|
);
|
|
365
396
|
} else {
|
|
@@ -372,9 +403,14 @@ function customOperationToGql(
|
|
|
372
403
|
}
|
|
373
404
|
|
|
374
405
|
if (Object.keys(fieldArgs).length > 0) {
|
|
375
|
-
const { gqlFields,
|
|
406
|
+
const { gqlFields, implicitTypes } = processFields(
|
|
407
|
+
typeName,
|
|
408
|
+
fieldArgs,
|
|
409
|
+
{},
|
|
410
|
+
{},
|
|
411
|
+
);
|
|
376
412
|
callSignature += `(${gqlFields.join(', ')})`;
|
|
377
|
-
|
|
413
|
+
implicitTypes.push(...implicitTypes);
|
|
378
414
|
}
|
|
379
415
|
|
|
380
416
|
const handler = handlers && handlers[0];
|
|
@@ -438,7 +474,8 @@ function customOperationToGql(
|
|
|
438
474
|
const gqlField = `${callSignature}: ${returnTypeName} ${gqlHandlerContent}${authString}`;
|
|
439
475
|
return {
|
|
440
476
|
gqlField,
|
|
441
|
-
|
|
477
|
+
implicitTypes: implicitTypes,
|
|
478
|
+
customTypeAuthRules,
|
|
442
479
|
lambdaFunctionDefinition,
|
|
443
480
|
customSqlDataSourceStrategy,
|
|
444
481
|
};
|
|
@@ -669,24 +706,33 @@ function calculateAuth(authorization: Authorization<any, any, any>[]) {
|
|
|
669
706
|
|
|
670
707
|
type AuthRule = ReturnType<typeof accessData>;
|
|
671
708
|
|
|
672
|
-
function
|
|
709
|
+
function validateCustomHandlerAuthRule(rule: AuthRule) {
|
|
673
710
|
if (rule.groups && rule.provider === 'oidc') {
|
|
674
711
|
throw new Error('OIDC group auth is not supported with a.handler.custom');
|
|
675
712
|
}
|
|
713
|
+
|
|
714
|
+
// not currently supported with handler.custom (JS Resolvers), but will be in the future
|
|
715
|
+
if (rule.provider === 'identityPool' || (rule.provider as string) === 'iam') {
|
|
716
|
+
throw new Error(
|
|
717
|
+
"identityPool-based auth (allow.guest() and allow.authenticated('identityPool')) is not supported with a.handler.custom",
|
|
718
|
+
);
|
|
719
|
+
}
|
|
676
720
|
}
|
|
677
721
|
|
|
678
|
-
function
|
|
722
|
+
function getAppSyncAuthDirectiveFromRule(rule: AuthRule): string {
|
|
679
723
|
const strategyDict: Record<string, Record<string, string>> = {
|
|
680
724
|
public: {
|
|
681
725
|
default: '@aws_api_key',
|
|
682
726
|
apiKey: '@aws_api_key',
|
|
683
727
|
iam: '@aws_iam',
|
|
728
|
+
identityPool: '@aws_iam',
|
|
684
729
|
},
|
|
685
730
|
private: {
|
|
686
731
|
default: '@aws_cognito_user_pools',
|
|
687
732
|
userPools: '@aws_cognito_user_pools',
|
|
688
733
|
oidc: '@aws_oidc',
|
|
689
734
|
iam: '@aws_iam',
|
|
735
|
+
identityPool: '@aws_iam',
|
|
690
736
|
},
|
|
691
737
|
groups: {
|
|
692
738
|
default: '@aws_cognito_user_pools',
|
|
@@ -718,28 +764,32 @@ function getCustomAuthProvider(rule: AuthRule): string {
|
|
|
718
764
|
return stratProvider;
|
|
719
765
|
}
|
|
720
766
|
|
|
721
|
-
function
|
|
722
|
-
|
|
767
|
+
function mapToNativeAppSyncAuthDirectives(
|
|
768
|
+
authorization: Authorization<any, any, any>[],
|
|
769
|
+
isCustomHandler: boolean,
|
|
770
|
+
) {
|
|
771
|
+
const rules = new Set<string>();
|
|
723
772
|
|
|
724
773
|
for (const entry of authorization) {
|
|
725
774
|
const rule = accessData(entry);
|
|
726
775
|
|
|
727
|
-
|
|
728
|
-
|
|
776
|
+
isCustomHandler && validateCustomHandlerAuthRule(rule);
|
|
777
|
+
|
|
778
|
+
const provider = getAppSyncAuthDirectiveFromRule(rule);
|
|
729
779
|
|
|
730
780
|
if (rule.groups) {
|
|
731
781
|
// example: (cognito_groups: ["Bloggers", "Readers"])
|
|
732
|
-
rules.
|
|
782
|
+
rules.add(
|
|
733
783
|
`${provider}(cognito_groups: [${rule.groups
|
|
734
784
|
.map((group) => `"${group}"`)
|
|
735
785
|
.join(', ')}])`,
|
|
736
786
|
);
|
|
737
787
|
} else {
|
|
738
|
-
rules.
|
|
788
|
+
rules.add(provider);
|
|
739
789
|
}
|
|
740
790
|
}
|
|
741
791
|
|
|
742
|
-
const authString = rules.join(' ');
|
|
792
|
+
const authString = [...rules].join(' ');
|
|
743
793
|
|
|
744
794
|
return { authString };
|
|
745
795
|
}
|
|
@@ -780,7 +830,9 @@ function processFields(
|
|
|
780
830
|
secondaryIndexes: TransformedSecondaryIndexes = {},
|
|
781
831
|
) {
|
|
782
832
|
const gqlFields: string[] = [];
|
|
783
|
-
|
|
833
|
+
// stores nested, field-level type definitions (custom types and enums)
|
|
834
|
+
// the need to be hoisted to top-level schema types and processed accordingly
|
|
835
|
+
const implicitTypes: [string, any][] = [];
|
|
784
836
|
|
|
785
837
|
validateImpliedFields(fields, impliedFields);
|
|
786
838
|
|
|
@@ -811,7 +863,7 @@ function processFields(
|
|
|
811
863
|
// enum type name conflicts
|
|
812
864
|
const enumName = `${capitalize(typeName)}${capitalize(fieldName)}`;
|
|
813
865
|
|
|
814
|
-
|
|
866
|
+
implicitTypes.push([enumName, fieldDef]);
|
|
815
867
|
|
|
816
868
|
gqlFields.push(
|
|
817
869
|
`${fieldName}: ${enumFieldToGql(enumName, secondaryIndexes[fieldName])}`,
|
|
@@ -823,7 +875,7 @@ function processFields(
|
|
|
823
875
|
fieldName,
|
|
824
876
|
)}`;
|
|
825
877
|
|
|
826
|
-
|
|
878
|
+
implicitTypes.push([customTypeName, fieldDef]);
|
|
827
879
|
|
|
828
880
|
gqlFields.push(`${fieldName}: ${customTypeName}`);
|
|
829
881
|
} else {
|
|
@@ -840,7 +892,7 @@ function processFields(
|
|
|
840
892
|
}
|
|
841
893
|
}
|
|
842
894
|
|
|
843
|
-
return { gqlFields,
|
|
895
|
+
return { gqlFields, implicitTypes };
|
|
844
896
|
}
|
|
845
897
|
|
|
846
898
|
type TransformedSecondaryIndexes = {
|
|
@@ -1045,6 +1097,46 @@ const getRefTypeForSchema = (schema: InternalSchema) => {
|
|
|
1045
1097
|
return getRefType;
|
|
1046
1098
|
};
|
|
1047
1099
|
|
|
1100
|
+
/**
|
|
1101
|
+
* Sorts top-level schema types to where Custom Types are processed last
|
|
1102
|
+
* This allows us to accrue and then apply inherited auth rules for custom types from custom operations
|
|
1103
|
+
* that reference them in their return values
|
|
1104
|
+
*/
|
|
1105
|
+
const sortTopLevelTypes = (topLevelTypes: [string, any][]) => {
|
|
1106
|
+
return topLevelTypes.sort(
|
|
1107
|
+
([_typeNameA, typeDefA], [_typeNameB, typeDefB]) => {
|
|
1108
|
+
if (
|
|
1109
|
+
(isCustomType(typeDefA) && isCustomType(typeDefB)) ||
|
|
1110
|
+
(!isCustomType(typeDefA) && !isCustomType(typeDefB))
|
|
1111
|
+
) {
|
|
1112
|
+
return 0;
|
|
1113
|
+
} else if (isCustomType(typeDefA) && !isCustomType(typeDefB)) {
|
|
1114
|
+
return 1;
|
|
1115
|
+
} else {
|
|
1116
|
+
return -1;
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
);
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Builds up dictionary of Custom Type name - array of inherited auth rules
|
|
1124
|
+
*/
|
|
1125
|
+
const mergeCustomTypeAuthRules = (
|
|
1126
|
+
existing: Record<string, Authorization<any, any, any>[]>,
|
|
1127
|
+
added: CustomTypeAuthRules,
|
|
1128
|
+
) => {
|
|
1129
|
+
if (!added) return;
|
|
1130
|
+
|
|
1131
|
+
const { typeName, authRules } = added;
|
|
1132
|
+
|
|
1133
|
+
if (typeName in existing) {
|
|
1134
|
+
existing[typeName] = [...existing[typeName], ...authRules];
|
|
1135
|
+
} else {
|
|
1136
|
+
existing[typeName] = authRules;
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
|
|
1048
1140
|
const schemaPreprocessor = (
|
|
1049
1141
|
schema: InternalSchema,
|
|
1050
1142
|
): {
|
|
@@ -1060,6 +1152,13 @@ const schemaPreprocessor = (
|
|
|
1060
1152
|
const customMutations = [];
|
|
1061
1153
|
const customSubscriptions = [];
|
|
1062
1154
|
|
|
1155
|
+
// Dict of auth rules to be applied to custom types
|
|
1156
|
+
// Inherited from the auth configured on the custom operations that return these custom types
|
|
1157
|
+
const customTypeInheritedAuthRules: Record<
|
|
1158
|
+
string,
|
|
1159
|
+
Authorization<any, any, any>[]
|
|
1160
|
+
> = {};
|
|
1161
|
+
|
|
1063
1162
|
const jsFunctions: JsResolver[] = [];
|
|
1064
1163
|
const lambdaFunctions: LambdaFunctionDefinition = {};
|
|
1065
1164
|
const customSqlDataSourceStrategies: CustomSqlDataSourceStrategy[] = [];
|
|
@@ -1071,7 +1170,8 @@ const schemaPreprocessor = (
|
|
|
1071
1170
|
|
|
1072
1171
|
const staticSchema =
|
|
1073
1172
|
schema.data.configuration.database.engine === 'dynamodb' ? false : true;
|
|
1074
|
-
|
|
1173
|
+
|
|
1174
|
+
const topLevelTypes = sortTopLevelTypes(Object.entries(schema.data.types));
|
|
1075
1175
|
|
|
1076
1176
|
const { schemaAuth, functionSchemaAccess } = extractFunctionSchemaAccess(
|
|
1077
1177
|
schema.data.authorization,
|
|
@@ -1116,7 +1216,15 @@ const schemaPreprocessor = (
|
|
|
1116
1216
|
),
|
|
1117
1217
|
);
|
|
1118
1218
|
|
|
1119
|
-
|
|
1219
|
+
let customAuth = '';
|
|
1220
|
+
if (typeName in customTypeInheritedAuthRules) {
|
|
1221
|
+
const { authString } = mapToNativeAppSyncAuthDirectives(
|
|
1222
|
+
customTypeInheritedAuthRules[typeName],
|
|
1223
|
+
false,
|
|
1224
|
+
);
|
|
1225
|
+
customAuth = authString;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1120
1228
|
const authFields = {};
|
|
1121
1229
|
|
|
1122
1230
|
const fieldLevelAuthRules = processFieldLevelAuthRules(
|
|
@@ -1124,25 +1232,26 @@ const schemaPreprocessor = (
|
|
|
1124
1232
|
authFields,
|
|
1125
1233
|
);
|
|
1126
1234
|
|
|
1127
|
-
const { gqlFields,
|
|
1235
|
+
const { gqlFields, implicitTypes } = processFields(
|
|
1128
1236
|
typeName,
|
|
1129
1237
|
fields,
|
|
1130
1238
|
authFields,
|
|
1131
1239
|
fieldLevelAuthRules,
|
|
1132
1240
|
);
|
|
1133
1241
|
|
|
1134
|
-
topLevelTypes.push(...
|
|
1242
|
+
topLevelTypes.push(...implicitTypes);
|
|
1135
1243
|
|
|
1136
1244
|
const joined = gqlFields.join('\n ');
|
|
1137
1245
|
|
|
1138
|
-
const model = `type ${typeName} ${
|
|
1246
|
+
const model = `type ${typeName} ${customAuth}\n{\n ${joined}\n}`;
|
|
1139
1247
|
gqlModels.push(model);
|
|
1140
1248
|
} else if (isCustomOperation(typeDef)) {
|
|
1141
1249
|
const { typeName: opType } = typeDef.data;
|
|
1142
1250
|
|
|
1143
1251
|
const {
|
|
1144
1252
|
gqlField,
|
|
1145
|
-
|
|
1253
|
+
implicitTypes,
|
|
1254
|
+
customTypeAuthRules,
|
|
1146
1255
|
jsFunctionForField,
|
|
1147
1256
|
lambdaFunctionDefinition,
|
|
1148
1257
|
customSqlDataSourceStrategy,
|
|
@@ -1154,9 +1263,13 @@ const schemaPreprocessor = (
|
|
|
1154
1263
|
getRefType,
|
|
1155
1264
|
);
|
|
1156
1265
|
|
|
1266
|
+
mergeCustomTypeAuthRules(
|
|
1267
|
+
customTypeInheritedAuthRules,
|
|
1268
|
+
customTypeAuthRules,
|
|
1269
|
+
);
|
|
1157
1270
|
Object.assign(lambdaFunctions, lambdaFunctionDefinition);
|
|
1158
1271
|
|
|
1159
|
-
topLevelTypes.push(...
|
|
1272
|
+
topLevelTypes.push(...implicitTypes);
|
|
1160
1273
|
|
|
1161
1274
|
if (jsFunctionForField) {
|
|
1162
1275
|
jsFunctions.push(jsFunctionForField);
|
|
@@ -1204,7 +1317,7 @@ const schemaPreprocessor = (
|
|
|
1204
1317
|
|
|
1205
1318
|
validateStaticFields(fields, authFields);
|
|
1206
1319
|
|
|
1207
|
-
const { gqlFields,
|
|
1320
|
+
const { gqlFields, implicitTypes } = processFields(
|
|
1208
1321
|
typeName,
|
|
1209
1322
|
fields,
|
|
1210
1323
|
authFields,
|
|
@@ -1213,7 +1326,7 @@ const schemaPreprocessor = (
|
|
|
1213
1326
|
partitionKey,
|
|
1214
1327
|
);
|
|
1215
1328
|
|
|
1216
|
-
topLevelTypes.push(...
|
|
1329
|
+
topLevelTypes.push(...implicitTypes);
|
|
1217
1330
|
|
|
1218
1331
|
const joined = gqlFields.join('\n ');
|
|
1219
1332
|
// TODO: update @model(timestamps: null) once a longer term solution gets
|
|
@@ -1255,7 +1368,7 @@ const schemaPreprocessor = (
|
|
|
1255
1368
|
authFields,
|
|
1256
1369
|
);
|
|
1257
1370
|
|
|
1258
|
-
const { gqlFields,
|
|
1371
|
+
const { gqlFields, implicitTypes } = processFields(
|
|
1259
1372
|
typeName,
|
|
1260
1373
|
fields,
|
|
1261
1374
|
authFields,
|
|
@@ -1264,7 +1377,7 @@ const schemaPreprocessor = (
|
|
|
1264
1377
|
partitionKey,
|
|
1265
1378
|
transformedSecondaryIndexes,
|
|
1266
1379
|
);
|
|
1267
|
-
topLevelTypes.push(...
|
|
1380
|
+
topLevelTypes.push(...implicitTypes);
|
|
1268
1381
|
|
|
1269
1382
|
const joined = gqlFields.join('\n ');
|
|
1270
1383
|
|
|
@@ -1521,7 +1634,8 @@ function transformCustomOperations(
|
|
|
1521
1634
|
|
|
1522
1635
|
const {
|
|
1523
1636
|
gqlField,
|
|
1524
|
-
|
|
1637
|
+
implicitTypes,
|
|
1638
|
+
customTypeAuthRules,
|
|
1525
1639
|
lambdaFunctionDefinition,
|
|
1526
1640
|
customSqlDataSourceStrategy,
|
|
1527
1641
|
} = customOperationToGql(
|
|
@@ -1535,7 +1649,8 @@ function transformCustomOperations(
|
|
|
1535
1649
|
|
|
1536
1650
|
return {
|
|
1537
1651
|
gqlField,
|
|
1538
|
-
|
|
1652
|
+
implicitTypes,
|
|
1653
|
+
customTypeAuthRules,
|
|
1539
1654
|
jsFunctionForField,
|
|
1540
1655
|
lambdaFunctionDefinition,
|
|
1541
1656
|
customSqlDataSourceStrategy,
|