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: allow api updates without migration #9864

Merged
merged 4 commits into from Mar 8, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 1 addition & 4 deletions packages/amplify-category-api/src/index.ts
Expand Up @@ -240,10 +240,7 @@ export const executeAmplifyHeadlessCommand = async (context: $TSContext, headles
break;
case 'update':
const resourceName = await getAppSyncApiResourceName(context);
if (!(await checkAppsyncApiResourceMigration(context, resourceName, true))) {
printer.error('Update operations only work on migrated projects. Run "amplify update api" and opt for migration.');
exitOnNextTick(0);
}
await checkAppsyncApiResourceMigration(context, resourceName, true);
await getCfnApiArtifactHandler(context).updateArtifacts(await validateUpdateApiRequest(headlessPayload));
break;
default:
Expand Down
Expand Up @@ -110,12 +110,12 @@ class CfnApiArtifactHandler implements ApiArtifactHandler {
}
const resourceDir = this.getResourceDir(apiName);
// update appsync cli-inputs
const appsyncCLIInputs = await this.updateAppsyncCLIInputs(updates, apiName);
const gqlSchemaPath = await this.updateAppsyncCLIInputs(updates, apiName);
if (updates.transformSchema) {
this.writeSchema(appsyncCLIInputs.serviceConfiguration.gqlSchemaPath, updates.transformSchema);
this.writeSchema(gqlSchemaPath, updates.transformSchema);
}
if (updates.conflictResolution) {
updates.conflictResolution = await this.createResolverResources(appsyncCLIInputs.serviceConfiguration.conflictResolution);
updates.conflictResolution = await this.createResolverResources(updates.conflictResolution);
await writeResolverConfig(updates.conflictResolution, resourceDir);
}

Expand Down Expand Up @@ -298,13 +298,24 @@ class CfnApiArtifactHandler implements ApiArtifactHandler {
return appsyncCLIInputs;
};

private updateAppsyncCLIInputs = async (updates: AppSyncServiceModification, apiName: string) => {
/**
* If the resource is migrated, updates cli-inputs.json with the specified updates
* If not migrated, this method is a noop (but still returns the schema path)
* @param updates The updates to apply
* @param apiName The api name
* @returns The gqlSchemaPath
*/
private updateAppsyncCLIInputs = async (updates: AppSyncServiceModification, apiName: string): Promise<string> => {
const cliState = new AppsyncApiInputState(apiName);
const gqlSchemaPath = path.join(this.getResourceDir(apiName), gqlSchemaFilename);
if (!cliState.cliInputFileExists()) {
return gqlSchemaPath;
}
const prevAppsyncInputs = cliState.getCLIInputPayload();

const appsyncInputs: AppSyncCLIInputs = prevAppsyncInputs;
if (!_.isEmpty(appsyncInputs.serviceConfiguration)) {
appsyncInputs.serviceConfiguration.gqlSchemaPath = path.join(this.getResourceDir(apiName), gqlSchemaFilename);
appsyncInputs.serviceConfiguration.gqlSchemaPath = gqlSchemaPath;
}
if (updates.conflictResolution) {
appsyncInputs.serviceConfiguration.conflictResolution = updates.conflictResolution;
Expand All @@ -316,7 +327,7 @@ class CfnApiArtifactHandler implements ApiArtifactHandler {
appsyncInputs.serviceConfiguration.additionalAuthTypes = updates.additionalAuthTypes;
}
await cliState.saveCLIInputPayload(appsyncInputs);
return appsyncInputs;
return gqlSchemaPath;
};
}

Expand Down
Expand Up @@ -463,10 +463,7 @@ export const updateWalkthrough = async (context: $TSContext): Promise<UpdateApiR
}

// migrate API project
if (!(await checkAppsyncApiResourceMigration(context, resourceName, true))) {
printer.error('Update operations only work on migrated projects. Run "amplify update api" and opt for migration.');
exitOnNextTick(0);
}
await checkAppsyncApiResourceMigration(context, resourceName, true);

// Get models
const project = await readProjectConfiguration(resourceDir);
Expand Down
18 changes: 14 additions & 4 deletions packages/amplify-e2e-core/src/categories/api.ts
@@ -1,7 +1,16 @@
import * as fs from 'fs-extra';
import _ from 'lodash';
import * as path from 'path';
import { addFeatureFlag, checkIfBucketExists, ExecutionContext, getCLIPath, getProjectMeta, nspawn as spawn, setTransformerVersionFlag, updateSchema } from '..';
import {
addFeatureFlag,
checkIfBucketExists,
ExecutionContext,
getCLIPath,
getProjectMeta,
nspawn as spawn,
setTransformerVersionFlag,
updateSchema,
} from '..';
import { multiSelect, singleSelect } from '../utils/selectors';
import { selectRuntime, selectTemplate } from './lambda-function';
import { modifiedApi } from './resources/modified-api-index';
Expand Down Expand Up @@ -280,12 +289,13 @@ export function updateApiSchema(cwd: string, projectName: string, schemaName: st
updateSchema(cwd, projectName, schemaText);
}

export function updateApiWithMultiAuth(cwd: string, settings: any) {
export function updateApiWithMultiAuth(cwd: string, settings?: { testingWithLatestCodebase?: boolean; doMigrate?: boolean }) {
return new Promise<void>((resolve, reject) => {
const testingWithLatestCodebase = settings?.testingWithLatestCodebase ?? false;
const chain = spawn(getCLIPath(testingWithLatestCodebase), ['update', 'api'], { cwd, stripColors: true });
chain.wait('Select from one of the below mentioned services:').sendCarriageReturn();
if (testingWithLatestCodebase === true) {
const doMigrate = settings?.doMigrate || testingWithLatestCodebase;
if (doMigrate) {
chain.wait('Do you want to migrate api resource').sendConfirmYes();
}
chain
Expand Down Expand Up @@ -883,4 +893,4 @@ export async function validateRestApiMeta(projRoot: string, meta?: any) {
seenAtLeastOneFunc = true;
}
expect(seenAtLeastOneFunc).toBe(true);
};
}
Expand Up @@ -7,7 +7,6 @@ import {
deleteProjectDir,
getAppSyncApi,
getCLIInputs,
getProjectConfig,
getProjectMeta,
getProjectSchema,
getSchemaPath,
Expand Down Expand Up @@ -86,6 +85,56 @@ describe('api migration update test', () => {
expect(graphqlApi.apiId).toEqual(GraphQLAPIIdOutput);
});

// This test is the same as the one above except the api is NOT migrated on update.
// This checks that new versions of the CLI can still update non-migrated APIs
it('allows api updates without api migration', async () => {
// init and add api with installed CLI
await initJSProjectWithProfile(projRoot, { name: 'simplemodelmultiauth' });
await addApiWithoutSchemaOldDx(projRoot);
await updateApiSchema(projRoot, 'simplemodelmultiauth', 'simple_model.graphql');
await amplifyPush(projRoot);
// update and push with codebase
await updateApiWithMultiAuth(projRoot, { testingWithLatestCodebase: true, doMigrate: false });
// 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
Expand Down