forked from aws-amplify/amplify-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: headless api migration (aws-amplify#8992)
- Loading branch information
Showing
7 changed files
with
274 additions
and
10 deletions.
There are no files selected for viewing
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
15 changes: 15 additions & 0 deletions
15
...ages/amplify-category-api/src/provider-utils/awscloudformation/utils/getAppSyncApiName.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,15 @@ | ||
import { $TSContext, AmplifySupportedService } from 'amplify-cli-core'; | ||
|
||
export const getAppSyncApiResourceName = async (context: $TSContext): Promise<string> => { | ||
const { allResources } = await context.amplify.getResourceStatus(); | ||
const apiResource = allResources.filter((resource: { service: string }) => resource.service === AmplifySupportedService.APPSYNC); | ||
let apiResourceName; | ||
|
||
if (apiResource.length > 0) { | ||
const resource = apiResource[0]; | ||
apiResourceName = resource.resourceName; | ||
} else { | ||
throw new Error(`${AmplifySupportedService.APPSYNC} API does not exist. To add an api, use "amplify update api".`); | ||
} | ||
return apiResourceName; | ||
}; |
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
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
37 changes: 37 additions & 0 deletions
37
...on-tests/src/__tests__/migration_tests/overrides/__snapshots__/api-migration.test.ts.snap
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,37 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`api migration update test updates AppSync API in headless mode 1`] = ` | ||
Object { | ||
"ElasticsearchWarning": true, | ||
"ResolverConfig": Object { | ||
"project": Object { | ||
"ConflictDetection": "VERSION", | ||
"ConflictHandler": "OPTIMISTIC_CONCURRENCY", | ||
}, | ||
}, | ||
"Version": 5, | ||
} | ||
`; | ||
|
||
exports[`api migration update test updates AppSync API in headless mode 2`] = ` | ||
Object { | ||
"additionalAuthenticationProviders": Array [ | ||
Object { | ||
"apiKeyConfig": Object {}, | ||
"authenticationType": "API_KEY", | ||
}, | ||
], | ||
"defaultAuthentication": Object { | ||
"authenticationType": "AWS_IAM", | ||
}, | ||
} | ||
`; | ||
|
||
exports[`api migration update test updates AppSync API in headless mode 3`] = ` | ||
"type Todo @model { | ||
id: ID! | ||
content: String | ||
override: String | ||
} | ||
" | ||
`; |
190 changes: 190 additions & 0 deletions
190
...ges/amplify-migration-tests/src/__tests__/migration_tests/overrides/api-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,190 @@ | ||
import { | ||
addApiWithoutSchema, | ||
addApiWithBlankSchemaAndConflictDetection, | ||
amplifyPush, | ||
amplifyPushUpdate, | ||
createNewProjectDir, | ||
deleteProject, | ||
deleteProjectDir, | ||
getAppSyncApi, | ||
getProjectMeta, | ||
getTransformConfig, | ||
updateApiSchema, | ||
updateApiWithMultiAuth, | ||
updateAPIWithResolutionStrategyWithModels, | ||
addHeadlessApi, | ||
updateHeadlessApi, | ||
getProjectSchema, | ||
getSchemaPath, | ||
getCLIInputs, | ||
initJSProjectWithProfile, | ||
} from 'amplify-e2e-core'; | ||
import { AddApiRequest, UpdateApiRequest } from 'amplify-headless-interface'; | ||
import * as fs from 'fs-extra'; | ||
import { TRANSFORM_BASE_VERSION, TRANSFORM_CURRENT_VERSION } from 'graphql-transformer-core'; | ||
import { join } from 'path'; | ||
|
||
describe('api migration update test', () => { | ||
let projRoot: string; | ||
beforeEach(async () => { | ||
projRoot = await createNewProjectDir('graphql-api'); | ||
}); | ||
|
||
afterEach(async () => { | ||
const metaFilePath = join(projRoot, 'amplify', '#current-cloud-backend', 'amplify-meta.json'); | ||
if (fs.existsSync(metaFilePath)) { | ||
await deleteProject(projRoot); | ||
} | ||
deleteProjectDir(projRoot); | ||
}); | ||
|
||
it('api update migration with multiauth', async () => { | ||
// init and add api with installed CLI | ||
await initJSProjectWithProfile(projRoot, { name: 'simplemodelmultiauth' }); | ||
await addApiWithoutSchema(projRoot); | ||
await updateApiSchema(projRoot, 'simplemodelmultiauth', 'simple_model.graphql'); | ||
await amplifyPush(projRoot); | ||
// update and push with codebase | ||
await updateApiWithMultiAuth(projRoot, { testingWithLatestCodebase: true }); | ||
// cli-inputs should exist | ||
expect(getCLIInputs(projRoot, 'api', 'simplemodelmultiauth')).toBeDefined(); | ||
await amplifyPushUpdate(projRoot, undefined, true, true); | ||
|
||
const meta = getProjectMeta(projRoot); | ||
const { output } = meta.api.simplemodelmultiauth; | ||
const { GraphQLAPIIdOutput, GraphQLAPIEndpointOutput, GraphQLAPIKeyOutput } = output; | ||
const { graphqlApi } = await getAppSyncApi(GraphQLAPIIdOutput, meta.providers.awscloudformation.Region); | ||
|
||
expect(graphqlApi).toBeDefined(); | ||
expect(graphqlApi.authenticationType).toEqual('API_KEY'); | ||
expect(graphqlApi.additionalAuthenticationProviders).toHaveLength(3); | ||
expect(graphqlApi.additionalAuthenticationProviders).toHaveLength(3); | ||
|
||
const cognito = graphqlApi.additionalAuthenticationProviders.filter(a => a.authenticationType === 'AMAZON_COGNITO_USER_POOLS')[0]; | ||
|
||
expect(cognito).toBeDefined(); | ||
expect(cognito.userPoolConfig).toBeDefined(); | ||
|
||
const iam = graphqlApi.additionalAuthenticationProviders.filter(a => a.authenticationType === 'AWS_IAM')[0]; | ||
|
||
expect(iam).toBeDefined(); | ||
|
||
const oidc = graphqlApi.additionalAuthenticationProviders.filter(a => a.authenticationType === 'OPENID_CONNECT')[0]; | ||
|
||
expect(oidc).toBeDefined(); | ||
expect(oidc.openIDConnectConfig).toBeDefined(); | ||
expect(oidc.openIDConnectConfig.issuer).toEqual('https://facebook.com/'); | ||
expect(oidc.openIDConnectConfig.clientId).toEqual('clientId'); | ||
expect(oidc.openIDConnectConfig.iatTTL).toEqual(1000); | ||
expect(oidc.openIDConnectConfig.authTTL).toEqual(2000); | ||
|
||
expect(GraphQLAPIIdOutput).toBeDefined(); | ||
expect(GraphQLAPIEndpointOutput).toBeDefined(); | ||
expect(GraphQLAPIKeyOutput).toBeDefined(); | ||
|
||
expect(graphqlApi).toBeDefined(); | ||
expect(graphqlApi.apiId).toEqual(GraphQLAPIIdOutput); | ||
}); | ||
|
||
it('init a sync enabled project and update conflict resolution strategy', async () => { | ||
const name = `syncenabled`; | ||
// init and add api with locally installed cli | ||
await initJSProjectWithProfile(projRoot, { name }); | ||
await addApiWithBlankSchemaAndConflictDetection(projRoot); | ||
await updateApiSchema(projRoot, name, 'simple_model.graphql'); | ||
await amplifyPush(projRoot); | ||
let transformConfig = getTransformConfig(projRoot, name); | ||
expect(transformConfig).toBeDefined(); | ||
expect(transformConfig.ResolverConfig).toBeDefined(); | ||
expect(transformConfig.ResolverConfig.project).toBeDefined(); | ||
expect(transformConfig.ResolverConfig.project.ConflictDetection).toEqual('VERSION'); | ||
expect(transformConfig.ResolverConfig.project.ConflictHandler).toEqual('AUTOMERGE'); | ||
|
||
//update and push with codebase | ||
await updateAPIWithResolutionStrategyWithModels(projRoot, { testingWithLatestCodebase: true }); | ||
expect(getCLIInputs(projRoot, 'api', 'syncenabled')).toBeDefined(); | ||
transformConfig = getTransformConfig(projRoot, name); | ||
expect(transformConfig).toBeDefined(); | ||
expect(transformConfig.Version).toBeDefined(); | ||
expect(transformConfig.Version).toEqual(TRANSFORM_CURRENT_VERSION); | ||
expect(transformConfig.ResolverConfig).toBeDefined(); | ||
expect(transformConfig.ResolverConfig.project).toBeDefined(); | ||
expect(transformConfig.ResolverConfig.project.ConflictDetection).toEqual('VERSION'); | ||
expect(transformConfig.ResolverConfig.project.ConflictHandler).toEqual('OPTIMISTIC_CONCURRENCY'); | ||
|
||
await amplifyPushUpdate(projRoot, undefined, true, true); | ||
const meta = getProjectMeta(projRoot); | ||
const { output } = meta.api[name]; | ||
const { GraphQLAPIIdOutput, GraphQLAPIEndpointOutput, GraphQLAPIKeyOutput } = output; | ||
const { graphqlApi } = await getAppSyncApi(GraphQLAPIIdOutput, meta.providers.awscloudformation.Region); | ||
|
||
expect(GraphQLAPIIdOutput).toBeDefined(); | ||
expect(GraphQLAPIEndpointOutput).toBeDefined(); | ||
expect(GraphQLAPIKeyOutput).toBeDefined(); | ||
|
||
expect(graphqlApi).toBeDefined(); | ||
expect(graphqlApi.apiId).toEqual(GraphQLAPIIdOutput); | ||
}); | ||
|
||
const addApiRequest: AddApiRequest = { | ||
version: 1, | ||
serviceConfiguration: { | ||
serviceName: 'AppSync', | ||
apiName: 'myApiName', | ||
transformSchema: fs.readFileSync(getSchemaPath('simple_model.graphql'), 'utf8'), | ||
defaultAuthType: { | ||
mode: 'API_KEY', | ||
}, | ||
}, | ||
}; | ||
|
||
const updateApiRequest: UpdateApiRequest = { | ||
version: 1, | ||
serviceModification: { | ||
serviceName: 'AppSync', | ||
transformSchema: fs.readFileSync(getSchemaPath('simple_model_override.graphql'), 'utf8'), | ||
defaultAuthType: { | ||
mode: 'AWS_IAM', | ||
}, | ||
additionalAuthTypes: [ | ||
{ | ||
mode: 'API_KEY', | ||
}, | ||
], | ||
conflictResolution: { | ||
defaultResolutionStrategy: { | ||
type: 'OPTIMISTIC_CONCURRENCY', | ||
}, | ||
}, | ||
}, | ||
}; | ||
it('updates AppSync API in headless mode', async () => { | ||
const name = `simplemodelv${TRANSFORM_BASE_VERSION}`; | ||
await initJSProjectWithProfile(projRoot, {}); | ||
await addHeadlessApi(projRoot, addApiRequest, { | ||
allowDestructiveUpdates: false, | ||
testingWithLatestCodebase: false, | ||
}); | ||
await amplifyPush(projRoot); | ||
await updateHeadlessApi(projRoot, updateApiRequest, true); | ||
expect(getCLIInputs(projRoot, 'api', 'myApiName')).toBeDefined(); | ||
await amplifyPushUpdate(projRoot, undefined, undefined, true); | ||
|
||
//verify | ||
const meta = getProjectMeta(projRoot); | ||
const { output } = meta.api.myApiName; | ||
const { GraphQLAPIIdOutput, GraphQLAPIEndpointOutput, GraphQLAPIKeyOutput } = output; | ||
const { graphqlApi } = await getAppSyncApi(GraphQLAPIIdOutput, meta.providers.awscloudformation.Region); | ||
|
||
expect(GraphQLAPIIdOutput).toBeDefined(); | ||
expect(GraphQLAPIEndpointOutput).toBeDefined(); | ||
expect(GraphQLAPIKeyOutput).toBeDefined(); | ||
|
||
expect(graphqlApi).toBeDefined(); | ||
expect(graphqlApi.apiId).toEqual(GraphQLAPIIdOutput); | ||
|
||
expect(getTransformConfig(projRoot, 'myApiName')).toMatchSnapshot(); | ||
expect(output.authConfig).toMatchSnapshot(); | ||
expect(getProjectSchema(projRoot, 'myApiName')).toMatchSnapshot(); | ||
}); | ||
}); |