-
Notifications
You must be signed in to change notification settings - Fork 820
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(graphql-model-transformer): override resource logical id to fix v…
…1 to v2 transformer migration (#8597)
- Loading branch information
Showing
24 changed files
with
1,226 additions
and
813 deletions.
There are no files selected for viewing
19 changes: 19 additions & 0 deletions
19
packages/amplify-e2e-tests/schemas/transformer_migration/basic-model-v1.graphql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! | ||
} |
19 changes: 19 additions & 0 deletions
19
packages/amplify-e2e-tests/schemas/transformer_migration/basic-model-v2.graphql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
272 changes: 272 additions & 0 deletions
272
packages/amplify-e2e-tests/src/__tests__/transformer-migrations/model-migration.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | ||
|
||
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]" }) { | ||
} | ||
} | ||
`; | ||
|
||
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]" }) { | ||
} | ||
} | ||
`; | ||
|
||
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 { | ||
} | ||
} | ||
} | ||
`; | ||
|
||
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; | ||
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, | ||
}, | ||
}); | ||
}; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.