Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(graphql-model-transformer): override resource logical id to fix v1 to v2 transformer migration #8597

Merged
merged 1 commit into from
Oct 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
type Post @model {
id: ID!
title: String!
}

type Customer @model @key(fields: ["email"]) @key(name: "byUsername", fields: ["username"], queryField: "byUsername") {
email: String!
username: String
}

type Test @model(timestamps: { createdAt: "createdOn", updatedAt: "updatedOn" }) {
id: ID!
title: String!
}

type Rename @model(queries: { get: "rename" }, mutations: { create: "makeRename" }, subscriptions: null) {
id: ID!
title: String!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
type Post @model @auth(rules: [{ allow: public }]) {
id: ID!
title: String!
}

type Customer @model @auth(rules: [{ allow: public }]) {
email: String! @primaryKey
username: String @index(name: "byUsername", queryField: "byUsername")
}

type Test @model(timestamps: { createdAt: "createdOn", updatedAt: "updatedOn" }) @auth(rules: [{ allow: public }]) {
id: ID!
title: String!
}

type Rename @model(queries: { get: "rename" }, mutations: { create: "makeRename" }, subscriptions: null) @auth(rules: [{ allow: public }]) {
id: ID!
title: String!
}
30 changes: 30 additions & 0 deletions packages/amplify-e2e-tests/src/GraphQLClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import axios from 'axios';

export interface GraphQLLocation {
line: number;
column: number;
}
export interface GraphQLError {
message: string;
locations: GraphQLLocation[];
path: string[];
}
export interface GraphQLResponse {
data: any;
errors: GraphQLError[];
}
export class GraphQLClient {
constructor(private url: string, private headers: any) {}

async query(query: string, variables: any = {}): Promise<GraphQLResponse> {
const axRes = await axios.post<GraphQLResponse>(
this.url,
{
query,
variables,
},
{ headers: this.headers },
);
return axRes.data;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
import {
initJSProjectWithProfile,
deleteProject,
amplifyPush,
amplifyPushUpdate,
addFeatureFlag,
createRandomName,
addAuthWithDefault,
} from 'amplify-e2e-core';
import { addApiWithoutSchema, updateApiSchema, getProjectMeta } from 'amplify-e2e-core';
import { createNewProjectDir, deleteProjectDir } from 'amplify-e2e-core';
import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync';
import gql from 'graphql-tag';
(global as any).fetch = require('node-fetch');
lazpavel marked this conversation as resolved.
Show resolved Hide resolved

describe('transformer model migration test', () => {
let projRoot: string;
let projectName: string;

beforeEach(async () => {
projectName = createRandomName();
projRoot = await createNewProjectDir(createRandomName());
await initJSProjectWithProfile(projRoot, { name: projectName });
await addAuthWithDefault(projRoot, {});
});

afterEach(async () => {
await deleteProject(projRoot);
deleteProjectDir(projRoot);
});

it('migration of model key queries timestamps should succeed', async () => {
const modelSchemaV1 = 'transformer_migration/basic-model-v1.graphql';
const modelSchemaV2 = 'transformer_migration/basic-model-v2.graphql';

await addApiWithoutSchema(projRoot, { apiName: projectName });
await updateApiSchema(projRoot, projectName, modelSchemaV1);
await amplifyPush(projRoot);

let appSyncClient = getAppSyncClientFromProj(projRoot);

let createPostMutation = /* GraphQL */ `
mutation CreatePost {
createPost(input: { title: "Created in V1" }) {
id
}
}
`;

let createPostResult = await appSyncClient.mutate({
mutation: gql(createPostMutation),
fetchPolicy: 'no-cache',
});

expect(createPostResult.errors).toBeUndefined();
expect(createPostResult.data).toBeDefined();

let createCustomerMutation = /* GraphQL */ `
mutation CreateCustomer {
createCustomer(input: { email: "[email protected]" }) {
email
}
}
`;

let createCustomerResult = await appSyncClient.mutate({
mutation: gql(createCustomerMutation),
fetchPolicy: 'no-cache',
});

expect(createCustomerResult.errors).toBeUndefined();
expect(createCustomerResult.data).toBeDefined();

let createTestMutation = /* GraphQL */ `
mutation CreateTest {
createTest(input: { title: "Created in V1" }) {
id
}
}
`;

let createTestResult = await appSyncClient.mutate({
mutation: gql(createTestMutation),
fetchPolicy: 'no-cache',
});

expect(createTestResult.errors).toBeUndefined();
expect(createTestResult.data).toBeDefined();

let createRenameMutation = /* GraphQL */ `
mutation CreateRename {
makeRename(input: { title: "Created in V1" }) {
id
}
}
`;

let createRenameResult = await appSyncClient.mutate({
mutation: gql(createRenameMutation),
fetchPolicy: 'no-cache',
});

expect(createRenameResult.errors).toBeUndefined();
expect(createRenameResult.data).toBeDefined();

await addFeatureFlag(projRoot, 'graphqltransformer', 'transformerVersion', 2);
await addFeatureFlag(projRoot, 'graphqltransformer', 'useExperimentalPipelinedTransformer', true);

await updateApiSchema(projRoot, projectName, modelSchemaV2);
await amplifyPushUpdate(projRoot);

appSyncClient = getAppSyncClientFromProj(projRoot);

createPostMutation = /* GraphQL */ `
mutation CreatePost {
createPost(input: { title: "Created in V2" }) {
id
}
}
`;

createPostResult = await appSyncClient.mutate({
mutation: gql(createPostMutation),
fetchPolicy: 'no-cache',
});

expect(createPostResult.errors).toBeUndefined();
expect(createPostResult.data).toBeDefined();

createCustomerMutation = /* GraphQL */ `
mutation CreateCustomer {
createCustomer(input: { email: "[email protected]" }) {
email
}
}
`;

createCustomerResult = await appSyncClient.mutate({
mutation: gql(createCustomerMutation),
fetchPolicy: 'no-cache',
});

expect(createCustomerResult.errors).toBeUndefined();
expect(createCustomerResult.data).toBeDefined();

createTestMutation = /* GraphQL */ `
mutation CreateTest {
createTest(input: { title: "Created in V2" }) {
id
}
}
`;

createTestResult = await appSyncClient.mutate({
mutation: gql(createTestMutation),
fetchPolicy: 'no-cache',
});

expect(createTestResult.errors).toBeUndefined();
expect(createTestResult.data).toBeDefined();

createRenameMutation = /* GraphQL */ `
mutation CreateRename {
makeRename(input: { title: "Created in V2" }) {
id
}
}
`;

createRenameResult = await appSyncClient.mutate({
mutation: gql(createRenameMutation),
fetchPolicy: 'no-cache',
});

expect(createRenameResult.errors).toBeUndefined();
expect(createRenameResult.data).toBeDefined();

const postsQuery = /* GraphQL */ `
query ListPosts {
listPosts {
items {
id
title
}
}
}
`;

let queryResult = await appSyncClient.query({
query: gql(postsQuery),
fetchPolicy: 'no-cache',
});

expect(queryResult.errors).toBeUndefined();
expect(queryResult.data).toBeDefined();
expect((queryResult.data as any).listPosts.items.length).toEqual(2);

const customersQuery = /* GraphQL */ `
query ListCustomers {
listCustomers {
items {
email
}
}
}
`;

queryResult = await appSyncClient.query({
query: gql(customersQuery),
fetchPolicy: 'no-cache',
});

expect(queryResult.errors).toBeUndefined();
expect(queryResult.data).toBeDefined();
expect((queryResult.data as any).listCustomers.items.length).toEqual(2);

const testsQuery = /* GraphQL */ `
query ListTests {
listTests {
items {
id
title
}
}
}
`;

queryResult = await appSyncClient.query({
query: gql(testsQuery),
fetchPolicy: 'no-cache',
});

expect(queryResult.errors).toBeUndefined();
expect(queryResult.data).toBeDefined();
expect((queryResult.data as any).listTests.items.length).toEqual(2);

const renamesQuery = /* GraphQL */ `
query GetRename {
rename (id: "${(createRenameResult.data as any).makeRename.id}") {
id
title
}
}
`;

queryResult = await appSyncClient.query({
query: gql(renamesQuery),
fetchPolicy: 'no-cache',
});

expect(queryResult.errors).toBeUndefined();
expect(queryResult.data).toBeDefined();
});

const getAppSyncClientFromProj = (projRoot: string) => {
const meta = getProjectMeta(projRoot);
const region = meta['providers']['awscloudformation']['Region'] as string;
lazpavel marked this conversation as resolved.
Show resolved Hide resolved
const { output } = meta.api[projectName];
const url = output.GraphQLAPIEndpointOutput as string;
const apiKey = output.GraphQLAPIKeyOutput as string;

return new AWSAppSyncClient({
url,
region,
disableOffline: true,
auth: {
type: AUTH_TYPE.API_KEY,
apiKey,
},
});
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,15 @@ import {
} from 'graphql';
import { SubscriptionLevel, ModelDirectiveConfiguration } from '@aws-amplify/graphql-model-transformer';
import { AccessControlMatrix } from './accesscontrol';
import { getBaseType, makeDirective, makeField, makeNamedType, ResourceConstants, ModelResourceIDs } from 'graphql-transformer-common';
import {
getBaseType,
makeDirective,
makeField,
makeNamedType,
ResourceConstants,
ModelResourceIDs,
ResolverResourceIDs,
} from 'graphql-transformer-common';
import * as iam from '@aws-cdk/aws-iam';
import * as cdk from '@aws-cdk/core';
import {
Expand Down Expand Up @@ -649,6 +657,7 @@ Static group authorization should perform as expected.`,
new TransformerResolver(
typeName,
fieldName,
ResolverResourceIDs.ResolverResourceID(typeName, fieldName),
MappingTemplate.s3MappingTemplateFromString(fieldAuthExpression, `${typeName}.${fieldName}.req.vtl`),
MappingTemplate.s3MappingTemplateFromString(fieldResponse, `${typeName}.${fieldName}.res.vtl`),
['init'],
Expand Down
13 changes: 10 additions & 3 deletions packages/amplify-graphql-auth-transformer/src/utils/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from 'graphql-transformer-common';
import { RELATIONAL_DIRECTIVES } from './constants';
import { RelationalPrimaryMapConfig, RoleDefinition, SearchableConfig } from './definitions';
import md5 from 'md5';

export const collectFieldNames = (object: ObjectTypeDefinitionNode): Array<string> => {
return object.fields!.map((field: FieldDefinitionNode) => field.name.value);
Expand All @@ -47,9 +48,9 @@ export const getModelConfig = (directive: DirectiveNode, typeName: string, isDat
},
subscriptions: {
level: SubscriptionLevel.on,
onCreate: [toCamelCase(['onCreate', typeName])],
onDelete: [toCamelCase(['onDelete', typeName])],
onUpdate: [toCamelCase(['onUpdate', typeName])],
onCreate: [ensureValidSubscriptionName(toCamelCase(['onCreate', typeName]))],
onDelete: [ensureValidSubscriptionName(toCamelCase(['onDelete', typeName]))],
onUpdate: [ensureValidSubscriptionName(toCamelCase(['onUpdate', typeName]))],
},
timestamps: {
createdAt: 'createdAt',
Expand Down Expand Up @@ -335,3 +336,9 @@ export const getSubscriptionFieldNames = (

return fields;
};

const ensureValidSubscriptionName = (name: string): string => {
if (name.length <= 50) return name;

return name.slice(0, 45) + md5(name).slice(0, 5);
};
Loading