Skip to content

Commit

Permalink
fix: searchable fix and migration e2e tests (#8666)
Browse files Browse the repository at this point in the history
  • Loading branch information
sundersc committed Nov 4, 2021
1 parent 1f5dd5c commit d5f9397
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 4 deletions.
@@ -0,0 +1,6 @@
type Todo @model @searchable {
id: ID!
name: String!
description: String
count: Int
}
@@ -0,0 +1,6 @@
type Todo @model @searchable @auth(rules: [{ allow: public }]) {
id: ID!
name: String!
description: String
count: Int
}
@@ -0,0 +1,115 @@
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 gql from 'graphql-tag';
import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync';
(global as any).fetch = require('node-fetch');

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

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 searchable directive - search should return expected results', async () => {
const v1Schema = 'transformer_migration/searchable-v1.graphql';
const v2Schema = 'transformer_migration/searchable-v2.graphql';

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

appSyncClient = getAppSyncClientFromProj(projRoot);
await runAndValidateQuery('test1', 'test1', 10);

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

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

appSyncClient = getAppSyncClientFromProj(projRoot);
await runAndValidateQuery('test2', 'test2', 10);
});

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,
},
});
};

const fragments = [`fragment FullTodo on Todo { id name description count }`];

const runMutation = async (query: string) => {
try {
const q = [query, ...fragments].join('\n');
const response = await appSyncClient.mutate({
mutation: gql(q),
fetchPolicy: 'no-cache',
});
return response;
} catch (e) {
console.error(e);
return null;
}
};

const createEntry = async (name: string, description: string, count: number) => {
return await runMutation(getCreateTodosMutation(name, description, count));
};

function getCreateTodosMutation(
name: string,
description: string,
count: number,
): string {
return `mutation {
createTodo(input: {
name: "${name}"
description: "${description}"
count: ${count}
}) { ...FullTodo }
}`;
}

const runAndValidateQuery = async (name: string, description: string, count: number) => {
const response = await createEntry(name, description, count);
expect(response).toBeDefined();
expect(response.errors).toBeUndefined();
expect(response.data).toBeDefined();
expect(response.data.createTodo).toBeDefined();
}
});
Expand Up @@ -69,6 +69,10 @@ export class AccessControlMatrix {
return this.roles.includes(role);
}

public getName(): string {
return this.name;
}

public getRoles(): Array<string> {
return this.roles;
}
Expand Down
Expand Up @@ -585,10 +585,11 @@ Static group authorization should perform as expected.`,
): void => {
const acmFields = acm.getResources();
const modelFields = def.fields ?? [];
const name = acm.getName();
// only add readonly fields if they exist
const allowedAggFields = modelFields.map(f => f.name.value).filter(f => !acmFields.includes(f));
let leastAllowedFields = acmFields;
const resolver = ctx.resolvers.getResolver(typeName, fieldName) as TransformerResolverProvider;
const resolver = ctx.resolvers.getResolver('Search', toUpper(name)) as TransformerResolverProvider;
// to protect search and aggregation queries we need to collect all the roles which can query
// and the allowed fields to run field auth on aggregation queries
const readRoleDefinitions = acm.getRolesPerOperation('read').map(role => {
Expand Down
Expand Up @@ -72,7 +72,7 @@ export function createParametersStack(stack: Stack): Map<string, CfnParameter> {
OpenSearchAccessIAMRoleName,
new CfnParameter(stack, OpenSearchStreamingIAMRoleName, {
description: 'The name of the streaming lambda function IAM role.',
default: 'SearchableLambdaIAMRole',
default: 'SearchLambdaIAMRole',
}),
],

Expand Down
Expand Up @@ -135,7 +135,7 @@ export class SearchableModelTransformer extends TransformerPluginBase {
const resolver = context.resolvers.generateQueryResolver(
typeName,
def.fieldName,
ResolverResourceIDs.ResolverResourceID(typeName, def.fieldName),
ResolverResourceIDs.ElasticsearchSearchResolverResourceID(type),
datasource as DataSourceProvider,
MappingTemplate.s3MappingTemplateFromString(
requestTemplate(attributeName, getNonKeywordFields(def.node), false, type, keyFields),
Expand All @@ -151,7 +151,7 @@ export class SearchableModelTransformer extends TransformerPluginBase {
),
);
resolver.mapToStack(stack);
context.resolvers.addResolver(typeName, def.fieldName, resolver);
context.resolvers.addResolver('Search', toUpper(type), resolver);
}

createStackOutputs(stack, domain.domainEndpoint, context.api.apiId, domain.domainArn);
Expand Down

0 comments on commit d5f9397

Please sign in to comment.