Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions codebuild_specs/e2e_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,25 +81,25 @@ batch:
depend-on:
- publish_to_local_registry
- identifier: >-
api_5_schema_function_1_generate_ts_data_schema_resolvers_sync_query_datastore
api_5_schema_function_1_generate_ts_data_schema_index_projection_resolvers
buildspec: codebuild_specs/run_e2e_tests.yml
env:
compute-type: BUILD_GENERAL1_LARGE
variables:
NODE_OPTIONS: '--max-old-space-size=14848'
TEST_SUITE: >-
src/__tests__/api_5.test.ts|src/__tests__/schema-function-1.test.ts|src/__tests__/generate_ts_data_schema.test.ts|src/__tests__/resolvers.test.ts|src/__tests__/graphql-v2/sync_query_datastore.test.ts
CLI_REGION: ap-northeast-1
src/__tests__/api_5.test.ts|src/__tests__/schema-function-1.test.ts|src/__tests__/generate_ts_data_schema.test.ts|src/__tests__/graphql-v2/index-projection.test.ts|src/__tests__/resolvers.test.ts
CLI_REGION: ap-east-1
depend-on:
- publish_to_local_registry
- identifier: api_6_api_lambda_auth
- identifier: sync_query_datastore_api_6_api_lambda_auth
buildspec: codebuild_specs/run_e2e_tests.yml
env:
compute-type: BUILD_GENERAL1_LARGE
variables:
NODE_OPTIONS: '--max-old-space-size=14848'
TEST_SUITE: >-
src/__tests__/api_6.test.ts|src/__tests__/graphql-v2/api_lambda_auth.test.ts
src/__tests__/graphql-v2/sync_query_datastore.test.ts|src/__tests__/api_6.test.ts|src/__tests__/graphql-v2/api_lambda_auth.test.ts
CLI_REGION: ca-central-1
depend-on:
- publish_to_local_registry
Expand Down Expand Up @@ -680,7 +680,7 @@ batch:
variables:
NODE_OPTIONS: '--max-old-space-size=6656'
TEST_SUITE: src/__tests__/schema-auth-5.test.ts
CLI_REGION: ap-northeast-1
CLI_REGION: ap-northeast-2
depend-on:
- publish_to_local_registry
- identifier: schema_searchable
Expand Down
11 changes: 11 additions & 0 deletions packages/amplify-e2e-tests/schemas/index_projection_all.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
input AMPLIFY {
globalAuthRule: AuthRule = { allow: public }
}

type Product @model {
id: ID!
name: String!
category: String! @index(name: "byCategory", queryField: "productsByCategory", projection: { type: ALL })
price: Float!
inStock: Boolean!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
input AMPLIFY {
globalAuthRule: AuthRule = { allow: public }
}

type Product @model {
id: ID!
name: String!
category: String!
@index(name: "byCategory", queryField: "productsByCategory", projection: { type: INCLUDE, nonKeyAttributes: ["name", "price"] })
price: Float!
inStock: Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
input AMPLIFY {
globalAuthRule: AuthRule = { allow: public }
}

type Product @model {
id: ID!
name: String!
category: String! @index(name: "byCategory", queryField: "productsByCategory", projection: { type: KEYS_ONLY })
price: Float
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import {
initJSProjectWithProfile,
deleteProject,
amplifyPush,
addApiWithBlankSchema,
updateApiSchema,
createNewProjectDir,
deleteProjectDir,
getProjectMeta,
} from 'amplify-category-api-e2e-core';
import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync';
import gql from 'graphql-tag';

// to deal with bug in cognito-identity-js
(global as any).fetch = require('node-fetch');
// to deal with subscriptions in node env
(global as any).WebSocket = require('ws');

const projectName = 'indexprojection';
const providerName = 'awscloudformation';

describe('Index Projection Tests', () => {
let projRoot: string;

beforeEach(async () => {
projRoot = await createNewProjectDir(projectName);
await initJSProjectWithProfile(projRoot, { name: projectName });
await addApiWithBlankSchema(projRoot, { transformerVersion: 2 });
});

afterEach(async () => {
await deleteProject(projRoot);
deleteProjectDir(projRoot);
});

it('creates GSI with KEYS_ONLY projection and queries successfully', async () => {
updateApiSchema(projRoot, projectName, 'index_projection_keys_only.graphql');
await amplifyPush(projRoot);

const meta = getProjectMeta(projRoot);
const region = meta.providers[providerName].Region as string;
const { output } = meta.api[projectName];
const url = output.GraphQLAPIEndpointOutput as string;
const apiKey = output.GraphQLAPIKeyOutput as string;

const api = new AWSAppSyncClient({ url, region, disableOffline: true, auth: { type: AUTH_TYPE.API_KEY, apiKey } });

await api.mutate({
mutation: gql`
mutation CreateProduct($input: CreateProductInput!) {
createProduct(input: $input) {
id
name
category
}
}
`,
fetchPolicy: 'no-cache',
variables: { input: { name: 'Laptop', category: 'Electronics' } },
});

const result = await api.query({
query: gql`
query ProductsByCategory($category: String!) {
productsByCategory(category: $category) {
items {
id
category
}
}
}
`,
fetchPolicy: 'no-cache',
variables: { category: 'Electronics' },
});

expect((result as any).data.productsByCategory.items.length).toEqual(1);
expect((result as any).data.productsByCategory.items[0].category).toEqual('Electronics');
});

it('creates GSI with INCLUDE projection and queries projected fields', async () => {
updateApiSchema(projRoot, projectName, 'index_projection_include.graphql');
await amplifyPush(projRoot);

const meta = getProjectMeta(projRoot);
const region = meta.providers[providerName].Region as string;
const { output } = meta.api[projectName];
const url = output.GraphQLAPIEndpointOutput as string;
const apiKey = output.GraphQLAPIKeyOutput as string;

const api = new AWSAppSyncClient({ url, region, disableOffline: true, auth: { type: AUTH_TYPE.API_KEY, apiKey } });

await api.mutate({
mutation: gql`
mutation CreateProduct($input: CreateProductInput!) {
createProduct(input: $input) {
id
name
category
price
}
}
`,
fetchPolicy: 'no-cache',
variables: { input: { name: 'Phone', category: 'Electronics', price: 999.99 } },
});

const result = await api.query({
query: gql`
query ProductsByCategory($category: String!) {
productsByCategory(category: $category) {
items {
id
category
name
price
}
}
}
`,
fetchPolicy: 'no-cache',
variables: { category: 'Electronics' },
});

expect((result as any).data.productsByCategory.items.length).toEqual(1);
expect((result as any).data.productsByCategory.items[0].name).toEqual('Phone');
expect((result as any).data.productsByCategory.items[0].price).toEqual(999.99);
});

it('creates GSI with ALL projection (default behavior)', async () => {
updateApiSchema(projRoot, projectName, 'index_projection_all.graphql');
await amplifyPush(projRoot);

const meta = getProjectMeta(projRoot);
const region = meta.providers[providerName].Region as string;
const { output } = meta.api[projectName];
const url = output.GraphQLAPIEndpointOutput as string;
const apiKey = output.GraphQLAPIKeyOutput as string;

const api = new AWSAppSyncClient({ url, region, disableOffline: true, auth: { type: AUTH_TYPE.API_KEY, apiKey } });

await api.mutate({
mutation: gql`
mutation CreateProduct($input: CreateProductInput!) {
createProduct(input: $input) {
id
name
category
price
inStock
}
}
`,
fetchPolicy: 'no-cache',
variables: { input: { name: 'Tablet', category: 'Electronics', price: 499.99, inStock: true } },
});

const result = await api.query({
query: gql`
query ProductsByCategory($category: String!) {
productsByCategory(category: $category) {
items {
id
category
name
price
inStock
}
}
}
`,
fetchPolicy: 'no-cache',
variables: { category: 'Electronics' },
});

expect((result as any).data.productsByCategory.items.length).toEqual(1);
expect((result as any).data.productsByCategory.items[0].inStock).toEqual(true);
});

it('returns error when querying non-projected fields with INCLUDE projection', async () => {
updateApiSchema(projRoot, projectName, 'index_projection_include.graphql');
await amplifyPush(projRoot);

const meta = getProjectMeta(projRoot);
const region = meta.providers[providerName].Region as string;
const { output } = meta.api[projectName];
const url = output.GraphQLAPIEndpointOutput as string;
const apiKey = output.GraphQLAPIKeyOutput as string;

const api = new AWSAppSyncClient({ url, region, disableOffline: true, auth: { type: AUTH_TYPE.API_KEY, apiKey } });

await api.mutate({
mutation: gql`
mutation CreateProduct($input: CreateProductInput!) {
createProduct(input: $input) {
id
name
category
price
inStock
}
}
`,
fetchPolicy: 'no-cache',
variables: { input: { name: 'Phone', category: 'Electronics', price: 999.99, inStock: false } },
});

try {
await api.query({
query: gql`
query ProductsByCategory($category: String!) {
productsByCategory(category: $category) {
items {
id
category
inStock
}
}
}
`,
fetchPolicy: 'no-cache',
variables: { category: 'Electronics' },
});
fail('Expected query to fail when requesting non-projected field');
} catch (error: any) {
expect(error.graphQLErrors).toBeDefined();
expect(error.graphQLErrors.length).toBeGreaterThan(0);
}
});
});
Loading
Loading