Skip to content

Commit

Permalink
fix: allow api updates without migration
Browse files Browse the repository at this point in the history
  • Loading branch information
edwardfoyle committed Feb 26, 2022
1 parent 5238264 commit 50828b2
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 20 deletions.
9 changes: 5 additions & 4 deletions packages/amplify-category-api/src/index.ts
Expand Up @@ -240,10 +240,11 @@ 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);
// 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 getCfnApiArtifactHandler(context).updateArtifacts(await validateUpdateApiRequest(headlessPayload));
break;
default:
Expand Down
Expand Up @@ -103,19 +103,19 @@ class CfnApiArtifactHandler implements ApiArtifactHandler {
// TODO once the AddApiRequest contains multiple services this class should depend on an ApiArtifactHandler
// for each service and delegate to the correct one
updateArtifacts = async (request: UpdateApiRequest, opts?: ApiArtifactHandlerOptions): Promise<void> => {
const updates = request.serviceModification;
let updates = request.serviceModification;
const apiName = getAppSyncResourceName(stateManager.getMeta());
if (!apiName) {
throw new Error(`No AppSync API configured in the project. Use 'amplify add api' to create an API.`);
}
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 retusn 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,11 @@ 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);
// 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);
// }

// 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

0 comments on commit 50828b2

Please sign in to comment.