@aws-amplify/graphql-model-transformer 0.7.1-graphql-vnext-dev-preview.10 → 0.7.1-graphql-vnext-dev-preview.11
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/CHANGELOG.md +23 -0
- package/lib/definitions.d.ts +1 -0
- package/lib/definitions.d.ts.map +1 -1
- package/lib/definitions.js +2 -1
- package/lib/definitions.js.map +1 -1
- package/lib/graphql-model-transformer.d.ts.map +1 -1
- package/lib/graphql-model-transformer.js +22 -3
- package/lib/graphql-model-transformer.js.map +1 -1
- package/lib/graphql-types/common.d.ts +6 -2
- package/lib/graphql-types/common.d.ts.map +1 -1
- package/lib/graphql-types/common.js +65 -1
- package/lib/graphql-types/common.js.map +1 -1
- package/lib/resolvers/common.d.ts +1 -1
- package/lib/resolvers/common.d.ts.map +1 -1
- package/lib/resolvers/common.js +1 -2
- package/lib/resolvers/common.js.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/model-transformer.test.ts +55 -0
- package/src/definitions.ts +2 -0
- package/src/graphql-model-transformer.ts +28 -3
- package/src/graphql-types/common.ts +97 -1
- package/src/resolvers/common.ts +1 -3
- package/tsconfig.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-amplify/graphql-model-transformer",
|
|
3
|
-
"version": "0.7.1-graphql-vnext-dev-preview.
|
|
3
|
+
"version": "0.7.1-graphql-vnext-dev-preview.11",
|
|
4
4
|
"description": "Amplify graphql @model transformer",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"test-watch": "jest --watch"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@aws-amplify/graphql-transformer-core": "0.11.0-graphql-vnext-dev-preview.
|
|
32
|
-
"@aws-amplify/graphql-transformer-interfaces": "1.10.2-graphql-vnext-dev-preview.
|
|
31
|
+
"@aws-amplify/graphql-transformer-core": "0.11.0-graphql-vnext-dev-preview.11",
|
|
32
|
+
"@aws-amplify/graphql-transformer-interfaces": "1.10.2-graphql-vnext-dev-preview.11",
|
|
33
33
|
"@aws-cdk/assets": "~1.124.0",
|
|
34
34
|
"@aws-cdk/aws-applicationautoscaling": "~1.124.0",
|
|
35
35
|
"@aws-cdk/aws-appsync": "~1.124.0",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"constructs": "^3.3.125",
|
|
59
59
|
"graphql": "^14.5.8",
|
|
60
60
|
"graphql-mapping-template": "4.18.4",
|
|
61
|
-
"graphql-transformer-common": "4.20.
|
|
61
|
+
"graphql-transformer-common": "4.20.1-graphql-vnext-dev-preview.11",
|
|
62
62
|
"lodash": "^4.17.21",
|
|
63
63
|
"md5": "^2.3.0"
|
|
64
64
|
},
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
],
|
|
84
84
|
"collectCoverage": true
|
|
85
85
|
},
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "16e9cc68157644de729457cec7dd51a8ce353228"
|
|
87
87
|
}
|
|
@@ -1215,4 +1215,59 @@ describe('ModelTransformer: ', () => {
|
|
|
1215
1215
|
expect(todoStack).toBeDefined();
|
|
1216
1216
|
expect(todoStack.Parameters).toMatchObject(modelParams);
|
|
1217
1217
|
});
|
|
1218
|
+
|
|
1219
|
+
it('global auth enabled should add apiKey if not default mode of auth', () => {
|
|
1220
|
+
const validSchema = `
|
|
1221
|
+
type Post @model {
|
|
1222
|
+
id: ID!
|
|
1223
|
+
title: String!
|
|
1224
|
+
tags: [Tag]
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
type Tag {
|
|
1228
|
+
id: ID
|
|
1229
|
+
tags: [Tag]
|
|
1230
|
+
}`;
|
|
1231
|
+
const transformer = new GraphQLTransform({
|
|
1232
|
+
authConfig: {
|
|
1233
|
+
defaultAuthentication: {
|
|
1234
|
+
authenticationType: 'AMAZON_COGNITO_USER_POOLS',
|
|
1235
|
+
},
|
|
1236
|
+
additionalAuthenticationProviders: [
|
|
1237
|
+
{
|
|
1238
|
+
authenticationType: 'API_KEY',
|
|
1239
|
+
},
|
|
1240
|
+
],
|
|
1241
|
+
},
|
|
1242
|
+
sandboxModeEnabled: true,
|
|
1243
|
+
transformers: [new ModelTransformer()],
|
|
1244
|
+
});
|
|
1245
|
+
const out = transformer.transform(validSchema);
|
|
1246
|
+
expect(out).toBeDefined();
|
|
1247
|
+
|
|
1248
|
+
const schema = parse(out.schema);
|
|
1249
|
+
validateModelSchema(schema);
|
|
1250
|
+
|
|
1251
|
+
const postType = getObjectType(schema, 'Post')!;
|
|
1252
|
+
expect(postType).toBeDefined();
|
|
1253
|
+
expect(postType.directives).toBeDefined();
|
|
1254
|
+
expect(postType.directives!.some(dir => dir.name.value === 'aws_api_key')).toEqual(true);
|
|
1255
|
+
|
|
1256
|
+
const tagType = getObjectType(schema, 'Tag')!;
|
|
1257
|
+
expect(tagType).toBeDefined();
|
|
1258
|
+
expect(tagType.directives).toBeDefined();
|
|
1259
|
+
expect(tagType.directives!.some(dir => dir.name.value === 'aws_api_key')).toEqual(true);
|
|
1260
|
+
|
|
1261
|
+
// check operations
|
|
1262
|
+
const queryType = getObjectType(schema, 'Query')!;
|
|
1263
|
+
expect(queryType).toBeDefined();
|
|
1264
|
+
const mutationType = getObjectType(schema, 'Mutation')!;
|
|
1265
|
+
expect(mutationType).toBeDefined();
|
|
1266
|
+
const subscriptionType = getObjectType(schema, 'Subscription')!;
|
|
1267
|
+
expect(subscriptionType).toBeDefined();
|
|
1268
|
+
|
|
1269
|
+
for (const field of [...queryType.fields!, ...mutationType.fields!, ...subscriptionType.fields!]) {
|
|
1270
|
+
expect(field.directives!.some(dir => dir.name.value === 'aws_api_key')).toEqual(true);
|
|
1271
|
+
}
|
|
1272
|
+
});
|
|
1218
1273
|
});
|
package/src/definitions.ts
CHANGED
|
@@ -14,3 +14,5 @@ export const BOOLEAN_FUNCTIONS = new Set<string>(['attributeExists', 'attributeT
|
|
|
14
14
|
export const ATTRIBUTE_TYPES = ['binary', 'binarySet', 'bool', 'list', 'map', 'number', 'numberSet', 'string', 'stringSet', '_null'];
|
|
15
15
|
|
|
16
16
|
export const OPERATION_KEY = '__operation';
|
|
17
|
+
|
|
18
|
+
export const API_KEY_DIRECTIVE = 'aws_api_key';
|
|
@@ -53,8 +53,10 @@ import {
|
|
|
53
53
|
toPascalCase,
|
|
54
54
|
} from 'graphql-transformer-common';
|
|
55
55
|
import {
|
|
56
|
+
addDirectivesToOperation,
|
|
56
57
|
addModelConditionInputs,
|
|
57
58
|
createEnumModelFilters,
|
|
59
|
+
extendTypeWithDirectives,
|
|
58
60
|
makeCreateInputField,
|
|
59
61
|
makeDeleteInputField,
|
|
60
62
|
makeListQueryFilterInput,
|
|
@@ -62,6 +64,7 @@ import {
|
|
|
62
64
|
makeModelSortDirectionEnumObject,
|
|
63
65
|
makeMutationConditionInput,
|
|
64
66
|
makeUpdateInputField,
|
|
67
|
+
propagateApiKeyToNestedTypes,
|
|
65
68
|
} from './graphql-types';
|
|
66
69
|
import {
|
|
67
70
|
generateAuthExpressionForSandboxMode,
|
|
@@ -84,6 +87,7 @@ import {
|
|
|
84
87
|
import { FieldWrapper, InputObjectDefinitionWrapper, ObjectDefinitionWrapper } from './wrappers/object-definition-wrapper';
|
|
85
88
|
import { CfnRole } from '@aws-cdk/aws-iam';
|
|
86
89
|
import md5 from 'md5';
|
|
90
|
+
import { API_KEY_DIRECTIVE } from './definitions';
|
|
87
91
|
|
|
88
92
|
export type Nullable<T> = T | null;
|
|
89
93
|
export type OptionalAndNullable<T> = Partial<T>;
|
|
@@ -260,6 +264,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
260
264
|
this.ensureModelSortDirectionEnum(ctx);
|
|
261
265
|
for (const type of this.typesWithModelDirective) {
|
|
262
266
|
const def = ctx.output.getObject(type)!;
|
|
267
|
+
const hasAuth = def.directives!.some(dir => dir.name.value === 'auth');
|
|
263
268
|
|
|
264
269
|
// add Non Model type inputs
|
|
265
270
|
this.createNonModelInputs(ctx, def);
|
|
@@ -279,6 +284,24 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
279
284
|
if (ctx.isProjectUsingDataStore()) {
|
|
280
285
|
this.addModelSyncFields(ctx, type);
|
|
281
286
|
}
|
|
287
|
+
// global auth check
|
|
288
|
+
if (!hasAuth && ctx.sandboxModeEnabled && ctx.authConfig.defaultAuthentication.authenticationType !== 'API_KEY') {
|
|
289
|
+
const apiKeyDirArray = [makeDirective(API_KEY_DIRECTIVE, [])];
|
|
290
|
+
extendTypeWithDirectives(ctx, def.name.value, apiKeyDirArray);
|
|
291
|
+
propagateApiKeyToNestedTypes(ctx as TransformerContextProvider, def, new Set<string>());
|
|
292
|
+
for (let operationField of queryFields) {
|
|
293
|
+
const operationName = operationField.name.value;
|
|
294
|
+
addDirectivesToOperation(ctx, ctx.output.getQueryTypeName()!, operationName, apiKeyDirArray);
|
|
295
|
+
}
|
|
296
|
+
for (let operationField of mutationFields) {
|
|
297
|
+
const operationName = operationField.name.value;
|
|
298
|
+
addDirectivesToOperation(ctx, ctx.output.getMutationTypeName()!, operationName, apiKeyDirArray);
|
|
299
|
+
}
|
|
300
|
+
for (let operationField of subscriptionsFields) {
|
|
301
|
+
const operationName = operationField.name.value;
|
|
302
|
+
addDirectivesToOperation(ctx, ctx.output.getSubscriptionTypeName()!, operationName, apiKeyDirArray);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
282
305
|
}
|
|
283
306
|
};
|
|
284
307
|
|
|
@@ -307,10 +330,12 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
307
330
|
default:
|
|
308
331
|
throw new Error('Unknown query field type');
|
|
309
332
|
}
|
|
333
|
+
// TODO: add mechanism to add an auth like rule to all non auth @models
|
|
334
|
+
// this way we can just depend on auth to add the check
|
|
310
335
|
resolver.addToSlot(
|
|
311
336
|
'postAuth',
|
|
312
337
|
MappingTemplate.s3MappingTemplateFromString(
|
|
313
|
-
generateAuthExpressionForSandboxMode(context),
|
|
338
|
+
generateAuthExpressionForSandboxMode(context.sandboxModeEnabled),
|
|
314
339
|
`${query.typeName}.${query.fieldName}.{slotName}.{slotIndex}.req.vtl`,
|
|
315
340
|
),
|
|
316
341
|
);
|
|
@@ -337,7 +362,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
337
362
|
resolver.addToSlot(
|
|
338
363
|
'postAuth',
|
|
339
364
|
MappingTemplate.s3MappingTemplateFromString(
|
|
340
|
-
generateAuthExpressionForSandboxMode(context),
|
|
365
|
+
generateAuthExpressionForSandboxMode(context.sandboxModeEnabled),
|
|
341
366
|
`${mutation.typeName}.${mutation.fieldName}.{slotName}.{slotIndex}.req.vtl`,
|
|
342
367
|
),
|
|
343
368
|
);
|
|
@@ -385,7 +410,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
385
410
|
resolver.addToSlot(
|
|
386
411
|
'postAuth',
|
|
387
412
|
MappingTemplate.s3MappingTemplateFromString(
|
|
388
|
-
generateAuthExpressionForSandboxMode(context),
|
|
413
|
+
generateAuthExpressionForSandboxMode(context.sandboxModeEnabled),
|
|
389
414
|
`${subscription.typeName}.${subscription.fieldName}.{slotName}.{slotIndex}.req.vtl`,
|
|
390
415
|
),
|
|
391
416
|
);
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import { TransformerTransformSchemaStepContextProvider } from '@aws-amplify/graphql-transformer-interfaces';
|
|
1
|
+
import { TransformerContextProvider, TransformerTransformSchemaStepContextProvider } from '@aws-amplify/graphql-transformer-interfaces';
|
|
2
2
|
import {
|
|
3
|
+
DirectiveNode,
|
|
3
4
|
EnumTypeDefinitionNode,
|
|
4
5
|
FieldDefinitionNode,
|
|
5
6
|
InputObjectTypeDefinitionNode,
|
|
6
7
|
Kind,
|
|
8
|
+
NamedTypeNode,
|
|
7
9
|
ObjectTypeDefinitionNode,
|
|
8
10
|
TypeDefinitionNode,
|
|
9
11
|
} from 'graphql';
|
|
10
12
|
import {
|
|
13
|
+
blankObjectExtension,
|
|
11
14
|
DEFAULT_SCALARS,
|
|
15
|
+
extendFieldWithDirectives,
|
|
16
|
+
extensionWithDirectives,
|
|
17
|
+
getBaseType,
|
|
12
18
|
makeArgument,
|
|
13
19
|
makeDirective,
|
|
14
20
|
makeField,
|
|
@@ -29,6 +35,7 @@ import {
|
|
|
29
35
|
SIZE_CONDITIONS,
|
|
30
36
|
STRING_CONDITIONS,
|
|
31
37
|
STRING_FUNCTIONS,
|
|
38
|
+
API_KEY_DIRECTIVE,
|
|
32
39
|
} from '../definitions';
|
|
33
40
|
import {
|
|
34
41
|
EnumWrapper,
|
|
@@ -245,7 +252,96 @@ export function makeEnumFilterInput(fieldWrapper: FieldWrapper): InputObjectType
|
|
|
245
252
|
return input.serialize();
|
|
246
253
|
}
|
|
247
254
|
|
|
255
|
+
export const addDirectivesToField = (
|
|
256
|
+
ctx: TransformerTransformSchemaStepContextProvider,
|
|
257
|
+
typeName: string,
|
|
258
|
+
fieldName: string,
|
|
259
|
+
directives: Array<DirectiveNode>,
|
|
260
|
+
) => {
|
|
261
|
+
const type = ctx.output.getType(typeName) as ObjectTypeDefinitionNode;
|
|
262
|
+
if (type) {
|
|
263
|
+
const field = type.fields?.find(f => f.name.value === fieldName);
|
|
264
|
+
if (field) {
|
|
265
|
+
const newFields = [...type.fields!.filter(f => f.name.value !== field.name.value), extendFieldWithDirectives(field, directives)];
|
|
266
|
+
|
|
267
|
+
const newType = {
|
|
268
|
+
...type,
|
|
269
|
+
fields: newFields,
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
ctx.output.putType(newType);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
export const addDirectivesToOperation = (
|
|
278
|
+
ctx: TransformerTransformSchemaStepContextProvider,
|
|
279
|
+
typeName: string,
|
|
280
|
+
operationName: string,
|
|
281
|
+
directives: Array<DirectiveNode>,
|
|
282
|
+
) => {
|
|
283
|
+
// add directives to the given operation
|
|
284
|
+
addDirectivesToField(ctx, typeName, operationName, directives);
|
|
285
|
+
|
|
286
|
+
// add the directives to the result type of the operation
|
|
287
|
+
const type = ctx.output.getType(typeName) as ObjectTypeDefinitionNode;
|
|
288
|
+
if (type) {
|
|
289
|
+
const field = type.fields!.find(f => f.name.value === operationName);
|
|
290
|
+
|
|
291
|
+
if (field) {
|
|
292
|
+
const returnFieldType = field.type as NamedTypeNode;
|
|
293
|
+
|
|
294
|
+
if (returnFieldType.name) {
|
|
295
|
+
const returnTypeName = returnFieldType.name.value;
|
|
296
|
+
|
|
297
|
+
extendTypeWithDirectives(ctx, returnTypeName, directives);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
export const extendTypeWithDirectives = (
|
|
304
|
+
ctx: TransformerTransformSchemaStepContextProvider,
|
|
305
|
+
typeName: string,
|
|
306
|
+
directives: Array<DirectiveNode>,
|
|
307
|
+
): void => {
|
|
308
|
+
let objectTypeExtension = blankObjectExtension(typeName);
|
|
309
|
+
objectTypeExtension = extensionWithDirectives(objectTypeExtension, directives);
|
|
310
|
+
ctx.output.addObjectExtension(objectTypeExtension);
|
|
311
|
+
};
|
|
312
|
+
|
|
248
313
|
export function makeModelSortDirectionEnumObject(): EnumTypeDefinitionNode {
|
|
249
314
|
const name = 'ModelSortDirection';
|
|
250
315
|
return EnumWrapper.create(name, ['ASC', 'DESC']).serialize();
|
|
251
316
|
}
|
|
317
|
+
// the smaller version of it's @auth equivalent since we only support
|
|
318
|
+
// apikey as the only global auth rule
|
|
319
|
+
export const propagateApiKeyToNestedTypes = (
|
|
320
|
+
ctx: TransformerContextProvider,
|
|
321
|
+
def: ObjectTypeDefinitionNode,
|
|
322
|
+
seenNonModelTypes: Set<string>,
|
|
323
|
+
) => {
|
|
324
|
+
const nonModelTypePredicate = (fieldType: TypeDefinitionNode): TypeDefinitionNode | undefined => {
|
|
325
|
+
if (fieldType) {
|
|
326
|
+
if (fieldType.kind !== 'ObjectTypeDefinition') {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
const typeModel = fieldType.directives!.find(dir => dir.name.value === 'model');
|
|
330
|
+
return typeModel !== undefined ? undefined : fieldType;
|
|
331
|
+
}
|
|
332
|
+
return fieldType;
|
|
333
|
+
};
|
|
334
|
+
const nonModelFieldTypes = def
|
|
335
|
+
.fields!.map(f => ctx.output.getType(getBaseType(f.type)) as TypeDefinitionNode)
|
|
336
|
+
.filter(nonModelTypePredicate);
|
|
337
|
+
for (const nonModelFieldType of nonModelFieldTypes) {
|
|
338
|
+
const nonModelName = nonModelFieldType.name.value;
|
|
339
|
+
const hasSeenType = seenNonModelTypes.has(nonModelName);
|
|
340
|
+
const hasApiKey = nonModelFieldType.directives?.some(dir => dir.name.value === API_KEY_DIRECTIVE) ?? false;
|
|
341
|
+
if (!hasSeenType && !hasApiKey) {
|
|
342
|
+
seenNonModelTypes.add(nonModelName);
|
|
343
|
+
extendTypeWithDirectives(ctx, nonModelName, [makeDirective(API_KEY_DIRECTIVE, [])]);
|
|
344
|
+
propagateApiKeyToNestedTypes(ctx, nonModelFieldType as ObjectTypeDefinitionNode, seenNonModelTypes);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
};
|
package/src/resolvers/common.ts
CHANGED
|
@@ -96,10 +96,8 @@ export const generateResolverKey = (typeName: string, fieldName: string): string
|
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* Util function to generate sandbox mode expression
|
|
99
|
-
* @param ctx context to get sandbox mode
|
|
100
99
|
*/
|
|
101
|
-
export const generateAuthExpressionForSandboxMode = (
|
|
102
|
-
const enabled = ctx.resourceHelper.api.sandboxModeEnabled;
|
|
100
|
+
export const generateAuthExpressionForSandboxMode = (enabled: boolean): string => {
|
|
103
101
|
let exp;
|
|
104
102
|
|
|
105
103
|
if (enabled) exp = iff(notEquals(methodCall(ref('util.authType')), str(API_KEY)), methodCall(ref('util.unauthorized')));
|