in packages/appsync-modelgen-plugin/src/utils/process-connections.ts [121:248]
export function processConnections(
field: CodeGenField,
model: CodeGenModel,
modelMap: CodeGenModelMap,
): CodeGenFieldConnection | undefined {
const connectionDirective = field.directives.find(d => d.name === 'connection');
if (connectionDirective) {
const otherSide = modelMap[field.type];
const connectionFields = connectionDirective.arguments.fields || [];
const otherSideField = getConnectedField(field, model, otherSide);
const isNewField = !otherSide.fields.includes(otherSideField);
// if a type is connected using name, then graphql-connection-transformer adds a field to
// track the connection and that field is not part of the selection set
// but if the field are connected using fields argument in connection directive
// we are reusing the field and it should be preserved in selection set
const isConnectingFieldAutoCreated = connectionFields.length === 0;
if (!isNewField) {
// 2 way connection
if (field.isList && !otherSideField.isList) {
// Many to One
return {
kind: CodeGenConnectionType.HAS_MANY,
associatedWith: otherSideField,
isConnectingFieldAutoCreated,
connectedModel: otherSide,
};
} else if (!field.isList && otherSideField.isList) {
// One to Many
if (connectionFields.length > 1) {
// Todo: Move to a common function and update the error message
throw new Error('DataStore only support one key in field');
}
return {
kind: CodeGenConnectionType.BELONGS_TO,
connectedModel: otherSide,
isConnectingFieldAutoCreated,
targetName: connectionFields[0] || makeConnectionAttributeName(model.name, field.name),
};
} else if (!field.isList && !otherSideField.isList) {
// One to One
// Data store can only support models where 1:1 connection, one of the connection side should be
// Non null able to support the foreign key constrain.
if (!field.isNullable && otherSideField.isNullable) {
/*
# model
type Person { # hasOne
license: License;
}
# otherSide
type License { # belongsTo
person: Person!
}
*/
return {
kind: CodeGenConnectionType.BELONGS_TO,
connectedModel: otherSide,
isConnectingFieldAutoCreated,
targetName: connectionFields[0] || makeConnectionAttributeName(model.name, field.name),
};
} else if (field.isNullable && !otherSideField.isNullable) {
/*
# model
type License { # belongsTo
person: Person!
}
# otherSide
type Person { # hasOne
license: License;
}
*/
return {
kind: CodeGenConnectionType.HAS_ONE,
associatedWith: otherSideField,
connectedModel: otherSide,
isConnectingFieldAutoCreated,
targetName: connectionFields[0] || makeConnectionAttributeName(model.name, field.name),
};
} else {
/*
# model
type License { # belongsTo
person: Person!
}
# otherSide
type Person { # hasOne
license: License;
}
*/
throw new Error(
`DataStore does not support 1 to 1 connection with both sides of connection as optional field: ${model.name}.${field.name}`,
);
}
}
} else {
// one way connection
if (field.isList) {
const connectionFieldName = makeConnectionAttributeName(model.name, field.name);
const existingConnectionField = otherSide.fields.find(f => f.name === connectionFieldName);
return {
kind: CodeGenConnectionType.HAS_MANY,
connectedModel: otherSide,
isConnectingFieldAutoCreated,
associatedWith: existingConnectionField || {
name: connectionFieldName,
type: 'ID',
isList: false,
isNullable: true,
directives: [],
},
};
} else {
if (connectionFields.length > 1) {
// Todo: Update the message
throw new Error('DataStore only support one key in field');
}
return {
kind: CodeGenConnectionType.BELONGS_TO,
connectedModel: otherSide,
isConnectingFieldAutoCreated,
targetName: connectionFields[0] || makeConnectionAttributeName(model.name, field.name),
};
}
}
}
}