export function processConnections()

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),
        };
      }
    }
  }
}