handleDisparateFieldNamedTypes()

in composition/src/v1/federation/federation-factory.ts [1365:1487]


  handleDisparateFieldNamedTypes() {
    for (const [fieldCoordinates, subgraphNamesByNamedTypeName] of this.subgraphNamesByNamedTypeNameByFieldCoords) {
      const coordinates = fieldCoordinates.split(PERIOD);
      if (coordinates.length !== 2) {
        continue;
      }
      const compositeOutputData = this.parentDefinitionDataByTypeName.get(coordinates[0]);
      if (!compositeOutputData) {
        this.errors.push(undefinedTypeError(coordinates[0]));
        continue;
      }
      // This error should never happen
      if (
        compositeOutputData.kind !== Kind.INTERFACE_TYPE_DEFINITION &&
        compositeOutputData.kind !== Kind.OBJECT_TYPE_DEFINITION
      ) {
        this.errors.push(
          unexpectedNonCompositeOutputTypeError(coordinates[0], kindToTypeString(compositeOutputData.kind)),
        );
        continue;
      }
      const fieldData = compositeOutputData.fieldDataByFieldName.get(coordinates[1]);
      // This error should never happen
      if (!fieldData) {
        this.errors.push(unknownFieldDataError(fieldCoordinates));
        continue;
      }
      const interfaceDataByTypeName = new Map<string, InterfaceDefinitionData>();
      const objectTypeNames = new Set<string>();
      let unionTypeName = '';
      for (const namedTypeName of subgraphNamesByNamedTypeName.keys()) {
        if (BASE_SCALARS.has(namedTypeName)) {
          this.errors.push(incompatibleFederatedFieldNamedTypeError(fieldCoordinates, subgraphNamesByNamedTypeName));
          break;
        }
        const namedTypeData = this.parentDefinitionDataByTypeName.get(namedTypeName);
        // This error should never happen
        if (!namedTypeData) {
          this.errors.push(unknownNamedTypeError(fieldCoordinates, namedTypeName));
          break;
        }
        switch (namedTypeData.kind) {
          case Kind.INTERFACE_TYPE_DEFINITION: {
            interfaceDataByTypeName.set(namedTypeData.name, namedTypeData);
            break;
          }
          case Kind.OBJECT_TYPE_DEFINITION: {
            objectTypeNames.add(namedTypeData.name);
            /* Multiple shared Field instances can explicitly return the same Object named type across subgraphs.
             * However, the Field is invalid if *any* of the other shared Field instances return a different Object named
             * type, even if each of those Objects named types could be coerced into the same mutual abstract type.
             * This is because it would be impossible to return identical data from each subgraph if one shared Field
             * instance explicitly returns a different Object named type to another shared Field instance.
             */
            if (objectTypeNames.size > 1) {
              this.errors.push(
                incompatibleFederatedFieldNamedTypeError(fieldCoordinates, subgraphNamesByNamedTypeName),
              );
              continue;
            }
            break;
          }
          case Kind.UNION_TYPE_DEFINITION: {
            if (unionTypeName) {
              this.errors.push(
                incompatibleFederatedFieldNamedTypeError(fieldCoordinates, subgraphNamesByNamedTypeName),
              );
              continue;
            }
            unionTypeName = namedTypeName;
            break;
          }
          default: {
            this.errors.push(incompatibleFederatedFieldNamedTypeError(fieldCoordinates, subgraphNamesByNamedTypeName));
            break;
          }
        }
      }
      if (interfaceDataByTypeName.size < 0 && !unionTypeName) {
        this.errors.push(incompatibleFederatedFieldNamedTypeError(fieldCoordinates, subgraphNamesByNamedTypeName));
        continue;
      }
      /* Default to the Union type name.
       * If more than one type of abstract type is returned, an error will be propagated.
       */
      let abstractTypeName = unionTypeName;
      if (interfaceDataByTypeName.size > 0) {
        if (unionTypeName) {
          this.errors.push(incompatibleFederatedFieldNamedTypeError(fieldCoordinates, subgraphNamesByNamedTypeName));
          continue;
        }
        /* If there is more than one Interface, there must be an origin Interface.
         * This is the "mutual Interface" that all the other Interfaces implement.
         */
        for (const interfaceTypeName of interfaceDataByTypeName.keys()) {
          abstractTypeName = interfaceTypeName;
          for (const [comparisonTypeName, comparisonData] of interfaceDataByTypeName) {
            if (interfaceTypeName === comparisonTypeName) {
              continue;
            }
            if (!comparisonData.implementedInterfaceTypeNames.has(interfaceTypeName)) {
              abstractTypeName = '';
              break;
            }
          }
          if (abstractTypeName) {
            break;
          }
        }
      }
      /* If the abstract type is:
       * 1. An Interface: each returned Object types must implement that origin Interface.
       * 2. A Union: all returned Object types must be Member of that Union.
       * 3. Invalid (empty string): return an error
       */
      if (!this.shouldUpdateFederatedFieldAbstractNamedType(abstractTypeName, objectTypeNames)) {
        this.errors.push(incompatibleFederatedFieldNamedTypeError(fieldCoordinates, subgraphNamesByNamedTypeName));
        continue;
      }
      fieldData.namedTypeName = abstractTypeName;
      this.updateTypeNodeNamedType(fieldData.type, abstractTypeName);
    }
  }