Skip to content

Commit

Permalink
feat: add implicit fields to filter input (#2236)
Browse files Browse the repository at this point in the history
  • Loading branch information
dpilch authored Feb 28, 2024
1 parent ac27b2f commit f7ec601
Show file tree
Hide file tree
Showing 22 changed files with 3,467 additions and 1,551 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { parse } from 'graphql';
import { ModelTransformer } from '@aws-amplify/graphql-model-transformer';
import { PrimaryKeyTransformer, IndexTransformer } from '@aws-amplify/graphql-index-transformer';
import { validateModelSchema } from '@aws-amplify/graphql-transformer-core';
import { validateModelSchema, constructDataSourceStrategies, MYSQL_DB_TYPE } from '@aws-amplify/graphql-transformer-core';
import { ResourceConstants } from 'graphql-transformer-common';
import { AppSyncAuthConfiguration } from '@aws-amplify/graphql-transformer-interfaces';
import { HasManyTransformer } from '@aws-amplify/graphql-relational-transformer';
import { testTransform } from '@aws-amplify/graphql-transformer-test-utils';
import { testTransform, mockSqlDataSourceStrategy } from '@aws-amplify/graphql-transformer-test-utils';
import { AuthTransformer } from '../graphql-auth-transformer';
import { getField, getObjectType } from './test-helpers';

Expand Down Expand Up @@ -456,6 +456,110 @@ describe('owner based @auth', () => {
expect(out.resolvers['Query.listPosts.auth.1.req.vtl']).toMatchSnapshot();
});

it('should successfully transform simple valid schema with implicit fields', async () => {
const authConfig: AppSyncAuthConfiguration = {
defaultAuthentication: {
authenticationType: 'AMAZON_COGNITO_USER_POOLS',
},
additionalAuthenticationProviders: [],
};
const validSchema = `
type Todo @model @auth(rules: [{ allow: owner }]) {
content: String
}
`;

const out = testTransform({
schema: validSchema,
authConfig,
transformers: [new ModelTransformer(), new AuthTransformer()],
});
expect(out).toBeDefined();

validateModelSchema(parse(out.schema));
parse(out.schema);
expect(out.schema).toMatchSnapshot();
});

it('should not add duplicate implicit field when already included', async () => {
const authConfig: AppSyncAuthConfiguration = {
defaultAuthentication: {
authenticationType: 'AMAZON_COGNITO_USER_POOLS',
},
additionalAuthenticationProviders: [],
};
const validSchema = `
type Todo @model @auth(rules: [{ allow: owner }]) {
owner: String
content: String
}
`;

const out = testTransform({
schema: validSchema,
authConfig,
transformers: [new ModelTransformer(), new AuthTransformer()],
});
expect(out).toBeDefined();

validateModelSchema(parse(out.schema));
parse(out.schema);
expect(out.schema).toMatchSnapshot();
});

it('should successfully transform simple valid schema with implicit fields', async () => {
const authConfig: AppSyncAuthConfiguration = {
defaultAuthentication: {
authenticationType: 'AMAZON_COGNITO_USER_POOLS',
},
additionalAuthenticationProviders: [],
};
const validSchema = `
type Todo @model @auth(rules: [{ allow: owner }]) {
id: ID! @primaryKey
content: String
}
`;

const out = testTransform({
schema: validSchema,
authConfig,
transformers: [new ModelTransformer(), new AuthTransformer(), new PrimaryKeyTransformer()],
dataSourceStrategies: constructDataSourceStrategies(validSchema, mockSqlDataSourceStrategy()),
});
expect(out).toBeDefined();

validateModelSchema(parse(out.schema));
parse(out.schema);
expect(out.schema).toMatchSnapshot();
});

it('should not add subscription filter with implicit owner field when subscription is disabled', async () => {
const authConfig: AppSyncAuthConfiguration = {
defaultAuthentication: {
authenticationType: 'AMAZON_COGNITO_USER_POOLS',
},
additionalAuthenticationProviders: [],
};
const validSchema = `
type Todo @model(subscriptions: null) @auth(rules: [{ allow: owner }]) {
owner: String
content: String
}
`;

const out = testTransform({
schema: validSchema,
authConfig,
transformers: [new ModelTransformer(), new AuthTransformer()],
});
expect(out).toBeDefined();

validateModelSchema(parse(out.schema));
parse(out.schema);
expect(out.schema).toMatchSnapshot();
});

describe('with identity claim feature flag disabled', () => {
test('auth transformer validation happy case', () => {
const authConfig: AppSyncAuthConfiguration = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import {
isSqlModel,
getModelDataSourceNameForTypeName,
isModelType,
getFilterInputName,
getConditionInputName,
getSubscriptionFilterInputName,
getConnectionName,
InputFieldWrapper,
isDynamoDbModel,
} from '@aws-amplify/graphql-transformer-core';
import {
DataSourceProvider,
Expand Down Expand Up @@ -42,6 +48,7 @@ import {
getBaseType,
makeDirective,
makeField,
makeInputValueDefinition,
makeNamedType,
ResourceConstants,
ModelResourceIDs,
Expand Down Expand Up @@ -334,8 +341,41 @@ export class AuthTransformer extends TransformerAuthBase implements TransformerA
this.authModelConfig.forEach((acm, modelName) => {
const def = context.output.getObject(modelName)!;
const modelHasSearchable = def.directives.some((dir) => dir.name.value === 'searchable');
const ownerFields = getOwnerFields(acm);
if (isDynamoDbModel(context, modelName)) {
const filterInput = context.output.getInput(getFilterInputName(modelName));
if (filterInput) {
const updatedFilterInput = { ...filterInput, fields: [...filterInput.fields] };
ownerFields.forEach((ownerField) => {
if (!filterInput.fields.some((field) => field.name.value === ownerField)) {
updatedFilterInput.fields.push(makeInputValueDefinition(ownerField, makeNamedType('ModelStringInput')));
}
});
context.output.updateInput(updatedFilterInput);
}
const conditionInput = context.output.getInput(getConditionInputName(modelName));
if (conditionInput) {
const updatedConditionInput = { ...conditionInput, fields: [...conditionInput.fields] };
ownerFields.forEach((ownerField) => {
if (!conditionInput.fields.some((field) => field.name.value === ownerField)) {
updatedConditionInput.fields.push(makeInputValueDefinition(ownerField, makeNamedType('ModelStringInput')));
}
});
context.output.updateInput(updatedConditionInput);
}
const subscriptionFilterInput = context.output.getInput(getSubscriptionFilterInputName(modelName));
if (subscriptionFilterInput) {
const updatedSubscriptionFilterInput = { ...subscriptionFilterInput, fields: [...subscriptionFilterInput.fields] };
ownerFields.forEach((ownerField) => {
if (!subscriptionFilterInput.fields.some((field) => field.name.value === ownerField)) {
updatedSubscriptionFilterInput.fields.push(makeInputValueDefinition(ownerField, makeNamedType('ModelStringInput')));
}
});
context.output.updateInput(updatedSubscriptionFilterInput);
}
}
// collect ownerFields and them in the model
this.addFieldsToObject(context, modelName, getOwnerFields(acm));
this.addFieldsToObject(context, modelName, ownerFields);
// Get the directives we need to add to the GraphQL nodes
const providers = this.getAuthProviders(acm.getRoles());
const addDefaultIfNeeded = providers.length === 0 ? this.configuredAuthProviders.shouldAddDefaultServiceDirective : false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ type ModelTestConnection {
input ModelTestFilterInput {
id: ModelIDInput
stringValue: ModelStringInput
createdAt: ModelStringInput
updatedAt: ModelStringInput
and: [ModelTestFilterInput]
or: [ModelTestFilterInput]
not: ModelTestFilterInput
Expand All @@ -181,6 +183,8 @@ input ModelTestConditionInput {
and: [ModelTestConditionInput]
or: [ModelTestConditionInput]
not: ModelTestConditionInput
createdAt: ModelStringInput
updatedAt: ModelStringInput
}
input CreateTestInput {
Expand All @@ -206,6 +210,8 @@ type Mutation {
input ModelSubscriptionTestFilterInput {
id: ModelSubscriptionIDInput
stringValue: ModelSubscriptionStringInput
createdAt: ModelSubscriptionStringInput
updatedAt: ModelSubscriptionStringInput
and: [ModelSubscriptionTestFilterInput]
or: [ModelSubscriptionTestFilterInput]
}
Expand Down Expand Up @@ -429,6 +435,8 @@ input ModelPostFilterInput {
enumValue: ModelTagInput
awsTimeValue: ModelStringInput
awsDateTime: ModelStringInput
createdAt: ModelStringInput
updatedAt: ModelStringInput
and: [ModelPostFilterInput]
or: [ModelPostFilterInput]
not: ModelPostFilterInput
Expand Down Expand Up @@ -458,6 +466,8 @@ input ModelPostConditionInput {
and: [ModelPostConditionInput]
or: [ModelPostConditionInput]
not: ModelPostConditionInput
createdAt: ModelStringInput
updatedAt: ModelStringInput
}
input CreatePostInput {
Expand Down Expand Up @@ -525,6 +535,8 @@ input ModelSubscriptionPostFilterInput {
enumValue: ModelSubscriptionStringInput
awsTimeValue: ModelSubscriptionStringInput
awsDateTime: ModelSubscriptionStringInput
createdAt: ModelSubscriptionStringInput
updatedAt: ModelSubscriptionStringInput
and: [ModelSubscriptionPostFilterInput]
or: [ModelSubscriptionPostFilterInput]
}
Expand Down
2 changes: 1 addition & 1 deletion packages/amplify-graphql-model-transformer/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export function makeModelScalarFilterInputObject(type: string, supportsCondition
export const makeModelSortDirectionEnumObject: () => EnumTypeDefinitionNode;

// @public (undocumented)
export const makeMutationConditionInput: (ctx: TransformerTransformSchemaStepContextProvider, name: string, object: ObjectTypeDefinitionNode) => InputObjectTypeDefinitionNode;
export const makeMutationConditionInput: (ctx: TransformerTransformSchemaStepContextProvider, name: string, object: ObjectTypeDefinitionNode, modelDirectiveConfig: ModelDirectiveConfiguration) => InputObjectTypeDefinitionNode;

// @public (undocumented)
export function makeSizeInputType(): InputObjectTypeDefinitionNode;
Expand Down
Loading

0 comments on commit f7ec601

Please sign in to comment.