Skip to content

Commit

Permalink
fix(graphql-relational-transformer): fixed has one and belongs to rel…
Browse files Browse the repository at this point in the history
…ationship (#8679)
  • Loading branch information
lazpavel authored Nov 6, 2021
1 parent dbf9925 commit 8a390fb
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -276,19 +276,31 @@ test('creates belongs to relationship with implicit fields', () => {
expect(relatedField).toBeDefined();
expect(relatedField.type.kind).toEqual(Kind.NAMED_TYPE);

const createInput = schema.definitions.find((def: any) => def.name && def.name.value === 'CreateTest1Input') as any;
const create1Input = schema.definitions.find((def: any) => def.name && def.name.value === 'CreateTest1Input') as any;
expect(create1Input).toBeDefined();
expect(create1Input.fields.length).toEqual(3);
expect(create1Input.fields.find((f: any) => f.name.value === 'id')).toBeDefined();
expect(create1Input.fields.find((f: any) => f.name.value === 'friendID')).toBeDefined();
expect(create1Input.fields.find((f: any) => f.name.value === 'email')).toBeDefined();

const update1Input = schema.definitions.find((def: any) => def.name && def.name.value === 'UpdateTest1Input') as any;
expect(update1Input).toBeDefined();
expect(update1Input.fields.length).toEqual(3);
expect(update1Input.fields.find((f: any) => f.name.value === 'id')).toBeDefined();
expect(update1Input.fields.find((f: any) => f.name.value === 'friendID')).toBeDefined();
expect(update1Input.fields.find((f: any) => f.name.value === 'email')).toBeDefined();

const createInput = schema.definitions.find((def: any) => def.name && def.name.value === 'CreateTestInput') as any;
expect(createInput).toBeDefined();
expect(createInput.fields.length).toEqual(4);
expect(createInput.fields.length).toEqual(3);
expect(createInput.fields.find((f: any) => f.name.value === 'id')).toBeDefined();
expect(createInput.fields.find((f: any) => f.name.value === 'friendID')).toBeDefined();
expect(createInput.fields.find((f: any) => f.name.value === 'email')).toBeDefined();
expect(createInput.fields.find((f: any) => f.name.value === 'testOtherHalfId')).toBeDefined();

const updateInput = schema.definitions.find((def: any) => def.name && def.name.value === 'UpdateTest1Input') as any;
const updateInput = schema.definitions.find((def: any) => def.name && def.name.value === 'UpdateTestInput') as any;
expect(updateInput).toBeDefined();
expect(updateInput.fields.length).toEqual(4);
expect(updateInput.fields.length).toEqual(3);
expect(updateInput.fields.find((f: any) => f.name.value === 'id')).toBeDefined();
expect(updateInput.fields.find((f: any) => f.name.value === 'friendID')).toBeDefined();
expect(updateInput.fields.find((f: any) => f.name.value === 'email')).toBeDefined();
expect(createInput.fields.find((f: any) => f.name.value === 'testOtherHalfId')).toBeDefined();
});
Expand All @@ -299,7 +311,7 @@ test('regression test for implicit id field on related type', () => {
powerSourceID: ID
powerSource: PowerSource @hasOne(fields: ["powerSourceID"])
}
type PowerSource @model {
sourceID: ID!
chargerID: ID
Expand Down
74 changes: 25 additions & 49 deletions packages/amplify-graphql-relational-transformer/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,12 @@ import {
isListType,
isNonNullType,
isScalar,
makeArgument,
makeDirective,
makeField,
makeInputValueDefinition,
makeListType,
makeNamedType,
makeNonNullType,
makeScalarKeyConditionForType,
makeValueNode,
ModelResourceIDs,
toCamelCase,
toPascalCase,
Expand Down Expand Up @@ -126,22 +123,37 @@ export function ensureHasOneConnectionField(
if (!connectionAttributeName) {
connectionAttributeName = getConnectionAttributeName(object.name.value, field.name.value);
}

const typeObject = ctx.output.getType(object.name.value) as ObjectTypeDefinitionNode;
if (typeObject) {
const updated = updateTypeWithConnectionField(typeObject, connectionAttributeName, isNonNullType(field.type));
ctx.output.putType(updated);
}

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

if (createInput) {
const updated = updateCreateInputWithConnectionField(createInput, connectionAttributeName, isNonNullType(field.type));

ctx.output.putType(updated);
ctx.output.putType(updateCreateInputWithConnectionField(createInput, connectionAttributeName, isNonNullType(field.type)));
}

const updateInputName = ModelResourceIDs.ModelUpdateInputObjectName(object.name.value);
const updateInput = ctx.output.getType(updateInputName) as InputObjectTypeDefinitionNode;

if (updateInput) {
const updated = updateUpdateInputWithConnectionField(updateInput, connectionAttributeName);
ctx.output.putType(updateUpdateInputWithConnectionField(updateInput, connectionAttributeName));
}

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

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

config.connectionFields.push(connectionAttributeName);
Expand All @@ -154,13 +166,8 @@ export function ensureHasOneConnectionField(
* but does not add additional fields as this will be handled by the hasMany directive
*/
export function ensureBelongsToConnectionField(config: BelongsToDirectiveConfiguration, ctx: TransformerContextProvider) {
const { relationType, relatedType, relatedField } = config;
if (relationType === 'hasOne') {
ensureHasOneConnectionField(config, ctx, getConnectionAttributeName(relatedType.name.value, relatedField.name.value));
} else {
// hasMany
config.connectionFields.push(getConnectionAttributeName(relatedType.name.value, relatedField.name.value));
}
const { relatedType, relatedField } = config;
config.connectionFields.push(getConnectionAttributeName(relatedType.name.value, relatedField.name.value));
}

export function ensureHasManyConnectionField(
Expand All @@ -175,15 +182,10 @@ export function ensureHasManyConnectionField(
}

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));
const updated = updateTypeWithConnectionField(relatedTypeObject, connectionAttributeName, isNonNullType(field.type));
ctx.output.putType(updated);
}

Expand Down Expand Up @@ -233,7 +235,7 @@ export function ensureHasManyConnectionField(
config.connectionFields.push(connectionFieldName);
}

function updateRelatedTypeWithConnectionField(
function updateTypeWithConnectionField(
object: ObjectTypeDefinitionNode,
connectionFieldName: string,
nonNull: boolean = false,
Expand All @@ -245,10 +247,9 @@ function updateRelatedTypeWithConnectionField(
return object;
}

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

return {
Expand Down Expand Up @@ -447,28 +448,3 @@ 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 8a390fb

Please sign in to comment.