@aws-amplify/graphql-model-transformer 0.9.3-beta.0 → 0.9.3
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 +12 -5
- 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 +2 -1
- package/lib/graphql-model-transformer.d.ts.map +1 -1
- package/lib/graphql-model-transformer.js +69 -17
- package/lib/graphql-model-transformer.js.map +1 -1
- package/lib/graphql-types/common.d.ts +7 -3
- package/lib/graphql-types/common.d.ts.map +1 -1
- package/lib/graphql-types/common.js +84 -20
- package/lib/graphql-types/common.js.map +1 -1
- package/lib/graphql-types/mutation.js +11 -11
- package/lib/graphql-types/mutation.js.map +1 -1
- package/lib/graphql-types/query.js +5 -5
- package/lib/graphql-types/query.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 +6 -6
- package/src/__tests__/__snapshots__/model-transformer.test.ts.snap +3 -3
- package/src/__tests__/model-transformer.test.ts +119 -15
- package/src/definitions.ts +2 -0
- package/src/graphql-model-transformer.ts +81 -9
- package/src/graphql-types/common.ts +98 -2
- package/src/graphql-types/mutation.ts +1 -1
- package/src/graphql-types/query.ts +1 -1
- package/src/resolvers/common.ts +1 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/wrappers/object-definition-wrapper.d.ts +0 -77
- package/lib/wrappers/object-definition-wrapper.d.ts.map +0 -1
- package/lib/wrappers/object-definition-wrapper.js +0 -354
- package/lib/wrappers/object-definition-wrapper.js.map +0 -1
- package/src/wrappers/object-definition-wrapper.ts +0 -447
|
@@ -411,7 +411,7 @@ describe('ModelTransformer: ', () => {
|
|
|
411
411
|
expect(defaultIdField).toBeDefined();
|
|
412
412
|
expect(getBaseType(defaultIdField.type)).toEqual('Int');
|
|
413
413
|
// It should not add default value for ctx.arg.id as id is of type Int
|
|
414
|
-
expect(result.
|
|
414
|
+
expect(result.resolvers['Mutation.createPost.req.vtl']).toMatchSnapshot();
|
|
415
415
|
});
|
|
416
416
|
|
|
417
417
|
it('should generate only create mutation', () => {
|
|
@@ -670,7 +670,7 @@ describe('ModelTransformer: ', () => {
|
|
|
670
670
|
validateModelSchema(schema);
|
|
671
671
|
});
|
|
672
672
|
|
|
673
|
-
it('should support timestamp parameters when generating
|
|
673
|
+
it('should support timestamp parameters when generating resolvers and output schema', () => {
|
|
674
674
|
const validSchema = `
|
|
675
675
|
type Post @model(timestamps: { createdAt: "createdOn", updatedAt: "updatedOn"}) {
|
|
676
676
|
id: ID!
|
|
@@ -688,8 +688,8 @@ describe('ModelTransformer: ', () => {
|
|
|
688
688
|
const schema = parse(result.schema);
|
|
689
689
|
validateModelSchema(schema);
|
|
690
690
|
|
|
691
|
-
expect(result.
|
|
692
|
-
expect(result.
|
|
691
|
+
expect(result.resolvers['Mutation.createPost.req.vtl']).toMatchSnapshot();
|
|
692
|
+
expect(result.resolvers['Mutation.updatePost.req.vtl']).toMatchSnapshot();
|
|
693
693
|
});
|
|
694
694
|
|
|
695
695
|
it('should not to auto generate createdAt and updatedAt when the type in schema is not AWSDateTime', () => {
|
|
@@ -712,8 +712,8 @@ describe('ModelTransformer: ', () => {
|
|
|
712
712
|
const schema = parse(result.schema);
|
|
713
713
|
validateModelSchema(schema);
|
|
714
714
|
|
|
715
|
-
expect(result.
|
|
716
|
-
expect(result.
|
|
715
|
+
expect(result.resolvers['Mutation.createPost.req.vtl']).toMatchSnapshot();
|
|
716
|
+
expect(result.resolvers['Mutation.updatePost.req.vtl']).toMatchSnapshot();
|
|
717
717
|
});
|
|
718
718
|
|
|
719
719
|
it('should have timestamps as nullable fields when the type makes it non-nullable', () => {
|
|
@@ -737,8 +737,8 @@ describe('ModelTransformer: ', () => {
|
|
|
737
737
|
const schema = parse(result.schema);
|
|
738
738
|
validateModelSchema(schema);
|
|
739
739
|
|
|
740
|
-
expect(result.
|
|
741
|
-
expect(result.
|
|
740
|
+
expect(result.resolvers['Mutation.createPost.req.vtl']).toMatchSnapshot();
|
|
741
|
+
expect(result.resolvers['Mutation.updatePost.req.vtl']).toMatchSnapshot();
|
|
742
742
|
});
|
|
743
743
|
|
|
744
744
|
it('should not to include createdAt and updatedAt field when timestamps is set to null', () => {
|
|
@@ -759,8 +759,8 @@ describe('ModelTransformer: ', () => {
|
|
|
759
759
|
const schema = parse(result.schema);
|
|
760
760
|
validateModelSchema(schema);
|
|
761
761
|
|
|
762
|
-
expect(result.
|
|
763
|
-
expect(result.
|
|
762
|
+
expect(result.resolvers['Mutation.createPost.req.vtl']).toMatchSnapshot();
|
|
763
|
+
expect(result.resolvers['Mutation.updatePost.req.vtl']).toMatchSnapshot();
|
|
764
764
|
});
|
|
765
765
|
|
|
766
766
|
it('should filter known input types from create and update input fields', () => {
|
|
@@ -908,7 +908,7 @@ describe('ModelTransformer: ', () => {
|
|
|
908
908
|
|
|
909
909
|
const definition = out.schema;
|
|
910
910
|
expect(definition).toBeDefined();
|
|
911
|
-
expect(out.
|
|
911
|
+
expect(out.resolvers).toMatchSnapshot();
|
|
912
912
|
|
|
913
913
|
validateModelSchema(parse(definition));
|
|
914
914
|
});
|
|
@@ -945,7 +945,7 @@ describe('ModelTransformer: ', () => {
|
|
|
945
945
|
|
|
946
946
|
const definition = out.schema;
|
|
947
947
|
expect(definition).toBeDefined();
|
|
948
|
-
expect(out.
|
|
948
|
+
expect(out.resolvers).toMatchSnapshot();
|
|
949
949
|
|
|
950
950
|
validateModelSchema(parse(definition));
|
|
951
951
|
});
|
|
@@ -979,7 +979,7 @@ describe('ModelTransformer: ', () => {
|
|
|
979
979
|
|
|
980
980
|
const definition = out.schema;
|
|
981
981
|
expect(definition).toBeDefined();
|
|
982
|
-
expect(out.
|
|
982
|
+
expect(out.resolvers).toMatchSnapshot();
|
|
983
983
|
|
|
984
984
|
validateModelSchema(parse(definition));
|
|
985
985
|
});
|
|
@@ -1115,8 +1115,8 @@ describe('ModelTransformer: ', () => {
|
|
|
1115
1115
|
const queryObject = getObjectType(schema, 'Query');
|
|
1116
1116
|
expectFields(queryObject!, ['syncTodos']);
|
|
1117
1117
|
// sync resolvers
|
|
1118
|
-
expect(out.
|
|
1119
|
-
expect(out.
|
|
1118
|
+
expect(out.resolvers['Query.syncTodos.req.vtl']).toMatchSnapshot();
|
|
1119
|
+
expect(out.resolvers['Query.syncTodos.res.vtl']).toMatchSnapshot();
|
|
1120
1120
|
// ds table
|
|
1121
1121
|
cdkExpect(out.rootStack).to(
|
|
1122
1122
|
haveResource('AWS::DynamoDB::Table', {
|
|
@@ -1166,4 +1166,108 @@ describe('ModelTransformer: ', () => {
|
|
|
1166
1166
|
}),
|
|
1167
1167
|
);
|
|
1168
1168
|
});
|
|
1169
|
+
|
|
1170
|
+
it('should add the model parameters at the root sack', () => {
|
|
1171
|
+
const modelParams = {
|
|
1172
|
+
DynamoDBModelTableReadIOPS: expect.objectContaining({
|
|
1173
|
+
Type: 'Number',
|
|
1174
|
+
Default: 5,
|
|
1175
|
+
Description: 'The number of read IOPS the table should support.',
|
|
1176
|
+
}),
|
|
1177
|
+
DynamoDBModelTableWriteIOPS: expect.objectContaining({
|
|
1178
|
+
Type: 'Number',
|
|
1179
|
+
Default: 5,
|
|
1180
|
+
Description: 'The number of write IOPS the table should support.',
|
|
1181
|
+
}),
|
|
1182
|
+
DynamoDBBillingMode: expect.objectContaining({
|
|
1183
|
+
Type: 'String',
|
|
1184
|
+
Default: 'PAY_PER_REQUEST',
|
|
1185
|
+
AllowedValues: ['PAY_PER_REQUEST', 'PROVISIONED'],
|
|
1186
|
+
Description: 'Configure @model types to create DynamoDB tables with PAY_PER_REQUEST or PROVISIONED billing modes.',
|
|
1187
|
+
}),
|
|
1188
|
+
DynamoDBEnablePointInTimeRecovery: expect.objectContaining({
|
|
1189
|
+
Type: 'String',
|
|
1190
|
+
Default: 'false',
|
|
1191
|
+
AllowedValues: ['true', 'false'],
|
|
1192
|
+
Description: 'Whether to enable Point in Time Recovery on the table.',
|
|
1193
|
+
}),
|
|
1194
|
+
DynamoDBEnableServerSideEncryption: expect.objectContaining({
|
|
1195
|
+
Type: 'String',
|
|
1196
|
+
Default: 'true',
|
|
1197
|
+
AllowedValues: ['true', 'false'],
|
|
1198
|
+
Description: 'Enable server side encryption powered by KMS.',
|
|
1199
|
+
}),
|
|
1200
|
+
};
|
|
1201
|
+
const validSchema = `type Todo @model {
|
|
1202
|
+
name: String
|
|
1203
|
+
}`;
|
|
1204
|
+
const transformer = new GraphQLTransform({
|
|
1205
|
+
sandboxModeEnabled: true,
|
|
1206
|
+
transformers: [new ModelTransformer()],
|
|
1207
|
+
});
|
|
1208
|
+
const out = transformer.transform(validSchema);
|
|
1209
|
+
|
|
1210
|
+
const rootStack = out.rootStack;
|
|
1211
|
+
expect(rootStack).toBeDefined();
|
|
1212
|
+
expect(rootStack.Parameters).toMatchObject(modelParams);
|
|
1213
|
+
|
|
1214
|
+
const todoStack = out.stacks['Todo'];
|
|
1215
|
+
expect(todoStack).toBeDefined();
|
|
1216
|
+
expect(todoStack.Parameters).toMatchObject(modelParams);
|
|
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
|
+
});
|
|
1169
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';
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
SyncConfig,
|
|
6
6
|
SyncUtils,
|
|
7
7
|
TransformerModelBase,
|
|
8
|
+
TransformerNestedStack,
|
|
9
|
+
FieldWrapper,
|
|
10
|
+
InputObjectDefinitionWrapper,
|
|
11
|
+
ObjectDefinitionWrapper,
|
|
8
12
|
} from '@aws-amplify/graphql-transformer-core';
|
|
9
13
|
import {
|
|
10
14
|
AppSyncDataSourceType,
|
|
@@ -20,6 +24,7 @@ import {
|
|
|
20
24
|
TransformerSchemaVisitStepContextProvider,
|
|
21
25
|
TransformerTransformSchemaStepContextProvider,
|
|
22
26
|
TransformerValidationStepContextProvider,
|
|
27
|
+
TransformerBeforeStepContextProvider,
|
|
23
28
|
} from '@aws-amplify/graphql-transformer-interfaces';
|
|
24
29
|
import { AttributeType, CfnTable, ITable, StreamViewType, Table, TableEncryption } from '@aws-cdk/aws-dynamodb';
|
|
25
30
|
import * as iam from '@aws-cdk/aws-iam';
|
|
@@ -51,8 +56,10 @@ import {
|
|
|
51
56
|
toPascalCase,
|
|
52
57
|
} from 'graphql-transformer-common';
|
|
53
58
|
import {
|
|
59
|
+
addDirectivesToOperation,
|
|
54
60
|
addModelConditionInputs,
|
|
55
61
|
createEnumModelFilters,
|
|
62
|
+
extendTypeWithDirectives,
|
|
56
63
|
makeCreateInputField,
|
|
57
64
|
makeDeleteInputField,
|
|
58
65
|
makeListQueryFilterInput,
|
|
@@ -60,6 +67,7 @@ import {
|
|
|
60
67
|
makeModelSortDirectionEnumObject,
|
|
61
68
|
makeMutationConditionInput,
|
|
62
69
|
makeUpdateInputField,
|
|
70
|
+
propagateApiKeyToNestedTypes,
|
|
63
71
|
} from './graphql-types';
|
|
64
72
|
import {
|
|
65
73
|
generateAuthExpressionForSandboxMode,
|
|
@@ -79,9 +87,9 @@ import {
|
|
|
79
87
|
generateListRequestTemplate,
|
|
80
88
|
generateSyncRequestTemplate,
|
|
81
89
|
} from './resolvers/query';
|
|
82
|
-
import { FieldWrapper, InputObjectDefinitionWrapper, ObjectDefinitionWrapper } from './wrappers/object-definition-wrapper';
|
|
83
90
|
import { CfnRole } from '@aws-cdk/aws-iam';
|
|
84
91
|
import md5 from 'md5';
|
|
92
|
+
import { API_KEY_DIRECTIVE } from './definitions';
|
|
85
93
|
|
|
86
94
|
export type Nullable<T> = T | null;
|
|
87
95
|
export type OptionalAndNullable<T> = Partial<T>;
|
|
@@ -167,6 +175,37 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
167
175
|
this.options = this.getOptions(options);
|
|
168
176
|
}
|
|
169
177
|
|
|
178
|
+
before = (ctx: TransformerBeforeStepContextProvider) => {
|
|
179
|
+
// add model related-parameters to the root stack
|
|
180
|
+
ctx.stackManager.addParameter(ResourceConstants.PARAMETERS.DynamoDBModelTableReadIOPS, {
|
|
181
|
+
description: 'The number of read IOPS the table should support.',
|
|
182
|
+
type: 'Number',
|
|
183
|
+
default: 5,
|
|
184
|
+
});
|
|
185
|
+
ctx.stackManager.addParameter(ResourceConstants.PARAMETERS.DynamoDBModelTableWriteIOPS, {
|
|
186
|
+
description: 'The number of write IOPS the table should support.',
|
|
187
|
+
type: 'Number',
|
|
188
|
+
default: 5,
|
|
189
|
+
});
|
|
190
|
+
ctx.stackManager.addParameter(ResourceConstants.PARAMETERS.DynamoDBBillingMode, {
|
|
191
|
+
description: 'Configure @model types to create DynamoDB tables with PAY_PER_REQUEST or PROVISIONED billing modes.',
|
|
192
|
+
default: 'PAY_PER_REQUEST',
|
|
193
|
+
allowedValues: ['PAY_PER_REQUEST', 'PROVISIONED'],
|
|
194
|
+
});
|
|
195
|
+
ctx.stackManager.addParameter(ResourceConstants.PARAMETERS.DynamoDBEnablePointInTimeRecovery, {
|
|
196
|
+
description: 'Whether to enable Point in Time Recovery on the table.',
|
|
197
|
+
type: 'String',
|
|
198
|
+
default: 'false',
|
|
199
|
+
allowedValues: ['true', 'false'],
|
|
200
|
+
});
|
|
201
|
+
ctx.stackManager.addParameter(ResourceConstants.PARAMETERS.DynamoDBEnableServerSideEncryption, {
|
|
202
|
+
description: 'Enable server side encryption powered by KMS.',
|
|
203
|
+
type: 'String',
|
|
204
|
+
default: 'true',
|
|
205
|
+
allowedValues: ['true', 'false'],
|
|
206
|
+
});
|
|
207
|
+
};
|
|
208
|
+
|
|
170
209
|
object = (definition: ObjectTypeDefinitionNode, directive: DirectiveNode, ctx: TransformerSchemaVisitStepContextProvider): void => {
|
|
171
210
|
const isTypeNameReserved =
|
|
172
211
|
definition.name.value === ctx.output.getQueryTypeName() ||
|
|
@@ -227,6 +266,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
227
266
|
this.ensureModelSortDirectionEnum(ctx);
|
|
228
267
|
for (const type of this.typesWithModelDirective) {
|
|
229
268
|
const def = ctx.output.getObject(type)!;
|
|
269
|
+
const hasAuth = def.directives!.some(dir => dir.name.value === 'auth');
|
|
230
270
|
|
|
231
271
|
// add Non Model type inputs
|
|
232
272
|
this.createNonModelInputs(ctx, def);
|
|
@@ -246,6 +286,24 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
246
286
|
if (ctx.isProjectUsingDataStore()) {
|
|
247
287
|
this.addModelSyncFields(ctx, type);
|
|
248
288
|
}
|
|
289
|
+
// global auth check
|
|
290
|
+
if (!hasAuth && ctx.sandboxModeEnabled && ctx.authConfig.defaultAuthentication.authenticationType !== 'API_KEY') {
|
|
291
|
+
const apiKeyDirArray = [makeDirective(API_KEY_DIRECTIVE, [])];
|
|
292
|
+
extendTypeWithDirectives(ctx, def.name.value, apiKeyDirArray);
|
|
293
|
+
propagateApiKeyToNestedTypes(ctx as TransformerContextProvider, def, new Set<string>());
|
|
294
|
+
for (let operationField of queryFields) {
|
|
295
|
+
const operationName = operationField.name.value;
|
|
296
|
+
addDirectivesToOperation(ctx, ctx.output.getQueryTypeName()!, operationName, apiKeyDirArray);
|
|
297
|
+
}
|
|
298
|
+
for (let operationField of mutationFields) {
|
|
299
|
+
const operationName = operationField.name.value;
|
|
300
|
+
addDirectivesToOperation(ctx, ctx.output.getMutationTypeName()!, operationName, apiKeyDirArray);
|
|
301
|
+
}
|
|
302
|
+
for (let operationField of subscriptionsFields) {
|
|
303
|
+
const operationName = operationField.name.value;
|
|
304
|
+
addDirectivesToOperation(ctx, ctx.output.getSubscriptionTypeName()!, operationName, apiKeyDirArray);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
249
307
|
}
|
|
250
308
|
};
|
|
251
309
|
|
|
@@ -274,10 +332,12 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
274
332
|
default:
|
|
275
333
|
throw new Error('Unknown query field type');
|
|
276
334
|
}
|
|
335
|
+
// TODO: add mechanism to add an auth like rule to all non auth @models
|
|
336
|
+
// this way we can just depend on auth to add the check
|
|
277
337
|
resolver.addToSlot(
|
|
278
338
|
'postAuth',
|
|
279
339
|
MappingTemplate.s3MappingTemplateFromString(
|
|
280
|
-
generateAuthExpressionForSandboxMode(context),
|
|
340
|
+
generateAuthExpressionForSandboxMode(context.sandboxModeEnabled),
|
|
281
341
|
`${query.typeName}.${query.fieldName}.{slotName}.{slotIndex}.req.vtl`,
|
|
282
342
|
),
|
|
283
343
|
);
|
|
@@ -304,7 +364,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
304
364
|
resolver.addToSlot(
|
|
305
365
|
'postAuth',
|
|
306
366
|
MappingTemplate.s3MappingTemplateFromString(
|
|
307
|
-
generateAuthExpressionForSandboxMode(context),
|
|
367
|
+
generateAuthExpressionForSandboxMode(context.sandboxModeEnabled),
|
|
308
368
|
`${mutation.typeName}.${mutation.fieldName}.{slotName}.{slotIndex}.req.vtl`,
|
|
309
369
|
),
|
|
310
370
|
);
|
|
@@ -352,7 +412,7 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
352
412
|
resolver.addToSlot(
|
|
353
413
|
'postAuth',
|
|
354
414
|
MappingTemplate.s3MappingTemplateFromString(
|
|
355
|
-
generateAuthExpressionForSandboxMode(context),
|
|
415
|
+
generateAuthExpressionForSandboxMode(context.sandboxModeEnabled),
|
|
356
416
|
`${subscription.typeName}.${subscription.fieldName}.{slotName}.{slotIndex}.req.vtl`,
|
|
357
417
|
),
|
|
358
418
|
);
|
|
@@ -1085,30 +1145,42 @@ export class ModelTransformer extends TransformerModelBase implements Transforme
|
|
|
1085
1145
|
description: 'The number of read IOPS the table should support.',
|
|
1086
1146
|
type: 'Number',
|
|
1087
1147
|
default: 5,
|
|
1088
|
-
})
|
|
1148
|
+
});
|
|
1089
1149
|
const writeIops = new cdk.CfnParameter(stack, ResourceConstants.PARAMETERS.DynamoDBModelTableWriteIOPS, {
|
|
1090
1150
|
description: 'The number of write IOPS the table should support.',
|
|
1091
1151
|
type: 'Number',
|
|
1092
1152
|
default: 5,
|
|
1093
|
-
})
|
|
1153
|
+
});
|
|
1094
1154
|
const billingMode = new cdk.CfnParameter(stack, ResourceConstants.PARAMETERS.DynamoDBBillingMode, {
|
|
1095
1155
|
description: 'Configure @model types to create DynamoDB tables with PAY_PER_REQUEST or PROVISIONED billing modes.',
|
|
1096
1156
|
type: 'String',
|
|
1097
1157
|
default: 'PAY_PER_REQUEST',
|
|
1098
1158
|
allowedValues: ['PAY_PER_REQUEST', 'PROVISIONED'],
|
|
1099
|
-
})
|
|
1159
|
+
});
|
|
1100
1160
|
const pointInTimeRecovery = new cdk.CfnParameter(stack, ResourceConstants.PARAMETERS.DynamoDBEnablePointInTimeRecovery, {
|
|
1101
1161
|
description: 'Whether to enable Point in Time Recovery on the table.',
|
|
1102
1162
|
type: 'String',
|
|
1103
1163
|
default: 'false',
|
|
1104
1164
|
allowedValues: ['true', 'false'],
|
|
1105
|
-
})
|
|
1165
|
+
});
|
|
1106
1166
|
const enableSSE = new cdk.CfnParameter(stack, ResourceConstants.PARAMETERS.DynamoDBEnableServerSideEncryption, {
|
|
1107
1167
|
description: 'Enable server side encryption powered by KMS.',
|
|
1108
1168
|
type: 'String',
|
|
1109
1169
|
default: 'true',
|
|
1110
1170
|
allowedValues: ['true', 'false'],
|
|
1111
|
-
})
|
|
1171
|
+
});
|
|
1172
|
+
// add the connection between the root and nested stack so the values can be passed down
|
|
1173
|
+
(stack as TransformerNestedStack).setParameter(readIops.node.id, cdk.Fn.ref(ResourceConstants.PARAMETERS.DynamoDBModelTableReadIOPS));
|
|
1174
|
+
(stack as TransformerNestedStack).setParameter(writeIops.node.id, cdk.Fn.ref(ResourceConstants.PARAMETERS.DynamoDBModelTableWriteIOPS));
|
|
1175
|
+
(stack as TransformerNestedStack).setParameter(billingMode.node.id, cdk.Fn.ref(ResourceConstants.PARAMETERS.DynamoDBBillingMode));
|
|
1176
|
+
(stack as TransformerNestedStack).setParameter(
|
|
1177
|
+
pointInTimeRecovery.node.id,
|
|
1178
|
+
cdk.Fn.ref(ResourceConstants.PARAMETERS.DynamoDBEnablePointInTimeRecovery),
|
|
1179
|
+
);
|
|
1180
|
+
(stack as TransformerNestedStack).setParameter(
|
|
1181
|
+
enableSSE.node.id,
|
|
1182
|
+
cdk.Fn.ref(ResourceConstants.PARAMETERS.DynamoDBEnableServerSideEncryption),
|
|
1183
|
+
);
|
|
1112
1184
|
|
|
1113
1185
|
// Add conditions.
|
|
1114
1186
|
// eslint-disable-next-line no-new
|
|
@@ -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,
|
|
@@ -36,7 +43,7 @@ import {
|
|
|
36
43
|
InputFieldWrapper,
|
|
37
44
|
InputObjectDefinitionWrapper,
|
|
38
45
|
ObjectDefinitionWrapper,
|
|
39
|
-
} from '
|
|
46
|
+
} from '@aws-amplify/graphql-transformer-core';
|
|
40
47
|
|
|
41
48
|
/**
|
|
42
49
|
* Creates the condition/filter input for a model
|
|
@@ -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
|
+
};
|
|
@@ -2,7 +2,7 @@ import { TransformerTransformSchemaStepContextProvider } from '@aws-amplify/grap
|
|
|
2
2
|
import { DocumentNode, InputObjectTypeDefinitionNode, ObjectTypeDefinitionNode } from 'graphql';
|
|
3
3
|
import { ModelResourceIDs, toPascalCase } from 'graphql-transformer-common';
|
|
4
4
|
import { ModelDirectiveConfiguration } from '../graphql-model-transformer';
|
|
5
|
-
import { InputFieldWrapper, InputObjectDefinitionWrapper, ObjectDefinitionWrapper } from '
|
|
5
|
+
import { InputFieldWrapper, InputObjectDefinitionWrapper, ObjectDefinitionWrapper } from '@aws-amplify/graphql-transformer-core';
|
|
6
6
|
import { makeConditionFilterInput } from './common';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TransformerTransformSchemaStepContextProvider } from '@aws-amplify/graphql-transformer-interfaces';
|
|
2
2
|
import { InputObjectTypeDefinitionNode, ObjectTypeDefinitionNode } from 'graphql';
|
|
3
|
-
import { FieldWrapper, ObjectDefinitionWrapper } from '
|
|
3
|
+
import { FieldWrapper, ObjectDefinitionWrapper } from '@aws-amplify/graphql-transformer-core';
|
|
4
4
|
import { makeConditionFilterInput } from './common';
|
|
5
5
|
export const makeListQueryFilterInput = (
|
|
6
6
|
ctx: TransformerTransformSchemaStepContextProvider,
|
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')));
|