Skip to content

Commit

Permalink
fix(graphql-relational-schema-transformer): has-many transformer upda…
Browse files Browse the repository at this point in the history
…te filter/condition inputs (#8565)
  • Loading branch information
lazpavel committed Oct 30, 2021
1 parent 1d8ff73 commit 9f5570b
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 1 deletion.
Expand Up @@ -509,6 +509,23 @@ test('has many with implicit index and fields and a user-defined primary key', (
expect(commentUpdateInput.fields.find((f: any) => f.name.value === 'id')).toBeDefined();
expect(commentUpdateInput.fields.find((f: any) => f.name.value === 'content')).toBeDefined();
expect(commentUpdateInput.fields.find((f: any) => f.name.value === 'postCommentsId')).toBeDefined();

const commentType = schema.definitions.find((def: any) => def.name && def.name.value === 'Comment') as any;
expect(commentType).toBeDefined();

const postCommentsField = commentType.fields.find((f: any) => f.name.value === 'postCommentsId');
expect(postCommentsField).toBeDefined();

const commentFilterInput = schema.definitions.find((def: any) => def.name && def.name.value === 'ModelCommentFilterInput') as any;
expect(commentFilterInput).toBeDefined();
expect(commentFilterInput.fields.find((f: any) => f.name.value === 'id')).toBeDefined();
expect(commentFilterInput.fields.find((f: any) => f.name.value === 'content')).toBeDefined();
expect(commentFilterInput.fields.find((f: any) => f.name.value === 'postCommentsId')).toBeDefined();

const commentConditionInput = schema.definitions.find((def: any) => def.name && def.name.value === 'ModelCommentConditionInput') as any;
expect(commentConditionInput).toBeDefined();
expect(commentConditionInput.fields.find((f: any) => f.name.value === 'content')).toBeDefined();
expect(commentConditionInput.fields.find((f: any) => f.name.value === 'postCommentsId')).toBeDefined();
});

test('the limit of 100 is used by default', () => {
Expand Down
99 changes: 98 additions & 1 deletion packages/amplify-graphql-relational-transformer/src/schema.ts
Expand Up @@ -10,14 +10,18 @@ import {
isListType,
isNonNullType,
isScalar,
makeArgument,
makeDirective,
makeField,
makeInputValueDefinition,
makeListType,
makeNamedType,
makeNonNullType,
makeScalarKeyConditionForType,
makeValueNode,
ModelResourceIDs,
toCamelCase,
toPascalCase,
toUpper,
} from 'graphql-transformer-common';
import {
Expand Down Expand Up @@ -170,8 +174,19 @@ export function ensureHasManyConnectionField(
return;
}

// Update the create and update input objects for this type.
const connectionAttributeName = getConnectionAttributeName(object.name.value, field.name.value);
const typeObject = ctx.output.getType(object.name.value) as ObjectTypeDefinitionNode;
if (typeObject) {
const updated = updateObjectWithHasManyDirectiveArguments(typeObject, field);
ctx.output.putType(updated);
}

const relatedTypeObject = ctx.output.getType(relatedType.name.value) as ObjectTypeDefinitionNode;
if (relatedTypeObject) {
const updated = updateRelatedTypeWithConnectionField(relatedTypeObject, connectionAttributeName, isNonNullType(field.type));
ctx.output.putType(updated);
}

const createInputName = ModelResourceIDs.ModelCreateInputObjectName(relatedType.name.value);
const createInput = ctx.output.getType(createInputName) as InputObjectTypeDefinitionNode;

Expand All @@ -190,6 +205,20 @@ export function ensureHasManyConnectionField(
ctx.output.putType(updated);
}

const filterInputName = toPascalCase(['Model', relatedType.name.value, 'FilterInput']);
const filterInput = ctx.output.getType(filterInputName) as InputObjectTypeDefinitionNode;
if (filterInput) {
const updated = updateFilterConnectionInputWithConnectionField(filterInput, connectionAttributeName);
ctx.output.putType(updated);
}

const conditionInputName = toPascalCase(['Model', relatedType.name.value, 'ConditionInput']);
const conditionInput = ctx.output.getType(conditionInputName) as InputObjectTypeDefinitionNode;
if (conditionInput) {
const updated = updateFilterConnectionInputWithConnectionField(conditionInput, connectionAttributeName);
ctx.output.putType(updated);
}

let connectionFieldName = 'id';

for (const field of object.fields!) {
Expand All @@ -204,6 +233,30 @@ export function ensureHasManyConnectionField(
config.connectionFields.push(connectionFieldName);
}

function updateRelatedTypeWithConnectionField(
object: ObjectTypeDefinitionNode,
connectionFieldName: string,
nonNull: boolean = false,
): ObjectTypeDefinitionNode {
const keyFieldExists = object.fields!.some(f => f.name.value === connectionFieldName);

// If the key field already exists then do not change the input.
if (keyFieldExists) {
return object;
}

const indexDirective = makeDirective('index', [makeArgument('name', makeValueNode(connectionFieldName))]);
const updatedFields = [
...object.fields!,
makeField(connectionFieldName, [], nonNull ? makeNonNullType(makeNamedType('ID')) : makeNamedType('ID'), [indexDirective]),
];

return {
...object,
fields: updatedFields,
};
}

function updateCreateInputWithConnectionField(
input: InputObjectTypeDefinitionNode,
connectionFieldName: string,
Expand Down Expand Up @@ -246,6 +299,25 @@ function updateUpdateInputWithConnectionField(
};
}

function updateFilterConnectionInputWithConnectionField(
input: InputObjectTypeDefinitionNode,
connectionFieldName: string,
): InputObjectTypeDefinitionNode {
const keyFieldExists = input.fields!.some(f => f.name.value === connectionFieldName);

// If the key field already exists then do not change the input.
if (keyFieldExists) {
return input;
}

const updatedFields = [...input.fields!, makeInputValueDefinition(connectionFieldName, makeNamedType('ModelIDInput'))];

return {
...input,
fields: updatedFields,
};
}

function makeModelConnectionField(config: HasManyDirectiveConfiguration): FieldDefinitionNode {
const { field, fields, indexName, relatedType, relatedTypeIndex } = config;
const args = [
Expand Down Expand Up @@ -375,3 +447,28 @@ export function getPartitionKeyField(object: ObjectTypeDefinitionNode): FieldDef

return fieldMap.get(name)!;
}

function updateObjectWithHasManyDirectiveArguments(object: ObjectTypeDefinitionNode, field: FieldDefinitionNode): ObjectTypeDefinitionNode {
const connectionAttributeName = getConnectionAttributeName(object.name.value, field.name.value);
const keyFieldExists = object.fields!.some(f => f.name.value === connectionAttributeName);

// If the key field already exists then do not change the input.
if (keyFieldExists) {
return object;
}

// directive updated for codegen to be able to lookup by indexName
const hasManyDirective = makeDirective('hasMany', [
makeArgument('indexName', makeValueNode(connectionAttributeName)),
makeArgument('fields', makeValueNode(['id'])),
]);
const updatedFields = [
...object.fields!.filter(it => it.name.value !== field.name.value),
makeField(field.name.value, [], field.type, [hasManyDirective]),
];

return {
...object,
fields: updatedFields,
};
}

0 comments on commit 9f5570b

Please sign in to comment.