missingSubgraphs: getEntriesNotInHashSet()

in composition/src/v1/federation/federation-factory.ts [1883:2308]


                missingSubgraphs: getEntriesNotInHashSet(
                  parentDefinitionData.subgraphNames,
                  inputValueData.subgraphNames,
                ),
                requiredSubgraphs: [...inputValueData.requiredSubgraphNames],
              });
            }
          }
          if (invalidRequiredInputs.length > 0) {
            this.errors.push(
              invalidRequiredInputValueError(INPUT_OBJECT, parentTypeName, invalidRequiredInputs, false),
            );
            break;
          }
          parentDefinitionData.node.fields = inputValueNodes;
          this.routerDefinitions.push(this.getNodeForRouterSchemaByData(parentDefinitionData));
          if (isNodeDataInaccessible(parentDefinitionData)) {
            this.validateReferencesOfInaccessibleType(parentDefinitionData);
            break;
          }
          if (clientInputValueNodes.length < 1) {
            this.errors.push(
              allChildDefinitionsAreInaccessibleError(
                kindToTypeString(parentDefinitionData.kind),
                parentTypeName,
                'input field',
              ),
            );
            break;
          }
          this.clientDefinitions.push({
            ...parentDefinitionData.node,
            directives: getClientPersistedDirectiveNodes(parentDefinitionData),
            fields: clientInputValueNodes,
          });
          break;
        case Kind.INTERFACE_TYPE_DEFINITION:
        // intentional fallthrough
        case Kind.OBJECT_TYPE_DEFINITION:
          const fieldNodes: Array<MutableFieldNode> = [];
          const clientSchemaFieldNodes: Array<MutableFieldNode> = [];
          const graphFieldDataByFieldName = new Map<string, GraphFieldData>();
          const invalidFieldNames = newInvalidFieldNames();
          const isObject = parentDefinitionData.kind === Kind.OBJECT_TYPE_DEFINITION;
          for (const [fieldName, fieldData] of parentDefinitionData.fieldDataByFieldName) {
            pushAuthorizationDirectives(fieldData, this.authorizationDataByParentTypeName.get(parentTypeName));
            const argumentNodes = this.getValidFieldArgumentNodes(fieldData);
            if (isObject) {
              validateExternalAndShareable(fieldData, invalidFieldNames);
            }
            fieldNodes.push(this.getNodeWithPersistedDirectivesByFieldData(fieldData, argumentNodes));
            if (isNodeDataInaccessible(fieldData)) {
              continue;
            }
            clientSchemaFieldNodes.push(getClientSchemaFieldNodeByFieldData(fieldData));
            graphFieldDataByFieldName.set(fieldName, this.fieldDataToGraphFieldData(fieldData));
          }
          if (isObject) {
            if (invalidFieldNames.byShareable.size > 0) {
              this.errors.push(invalidFieldShareabilityError(parentDefinitionData, invalidFieldNames.byShareable));
            }
            if (invalidFieldNames.subgraphNamesByExternalFieldName.size > 0) {
              this.errors.push(
                allExternalFieldInstancesError(parentTypeName, invalidFieldNames.subgraphNamesByExternalFieldName),
              );
            }
          }
          parentDefinitionData.node.fields = fieldNodes;
          this.internalGraph.initializeNode(parentTypeName, graphFieldDataByFieldName);
          // Implemented interfaces can only be validated after all fields are merged
          if (parentDefinitionData.implementedInterfaceTypeNames.size > 0) {
            interfaceImplementations.push({ data: parentDefinitionData, clientSchemaFieldNodes });
            break;
          }
          this.routerDefinitions.push(this.getNodeForRouterSchemaByData(parentDefinitionData));
          const isQuery = isNodeQuery(parentTypeName);
          if (isNodeDataInaccessible(parentDefinitionData)) {
            if (isQuery) {
              this.errors.push(inaccessibleQueryRootTypeError);
              break;
            }
            this.validateReferencesOfInaccessibleType(parentDefinitionData);
            this.internalGraph.setNodeInaccessible(parentDefinitionData.name);
            break;
          }
          if (clientSchemaFieldNodes.length < 1) {
            const error = isQuery
              ? noQueryRootTypeError(false)
              : allChildDefinitionsAreInaccessibleError(
                  kindToTypeString(parentDefinitionData.kind),
                  parentTypeName,
                  FIELD,
                );
            this.errors.push(error);
            break;
          }
          this.clientDefinitions.push({
            ...parentDefinitionData.node,
            directives: getClientPersistedDirectiveNodes(parentDefinitionData),
            fields: clientSchemaFieldNodes,
          });
          break;
        case Kind.SCALAR_TYPE_DEFINITION:
          if (BASE_SCALARS.has(parentTypeName)) {
            break;
          }
          this.routerDefinitions.push(this.getNodeForRouterSchemaByData(parentDefinitionData));
          if (isNodeDataInaccessible(parentDefinitionData)) {
            this.validateReferencesOfInaccessibleType(parentDefinitionData);
            this.internalGraph.setNodeInaccessible(parentDefinitionData.name);
            break;
          }
          this.clientDefinitions.push({
            ...parentDefinitionData.node,
            directives: getClientPersistedDirectiveNodes(parentDefinitionData),
          });
          break;
        case Kind.UNION_TYPE_DEFINITION:
          parentDefinitionData.node.types = mapToArrayOfValues(parentDefinitionData.memberByMemberTypeName);
          this.routerDefinitions.push(this.getNodeForRouterSchemaByData(parentDefinitionData));
          if (isNodeDataInaccessible(parentDefinitionData)) {
            this.validateReferencesOfInaccessibleType(parentDefinitionData);
            this.internalGraph.setNodeInaccessible(parentDefinitionData.name);
            break;
          }
          const clientMembers = this.getClientSchemaUnionMembers(parentDefinitionData);
          if (clientMembers.length < 1) {
            this.errors.push(allChildDefinitionsAreInaccessibleError(UNION, parentTypeName, 'union member type'));
            break;
          }
          this.clientDefinitions.push({
            ...parentDefinitionData.node,
            directives: getClientPersistedDirectiveNodes(parentDefinitionData),
            types: clientMembers,
          });
          break;
      }
    }
  }

  federateSubgraphData() {
    this.federateInternalSubgraphData();
    this.handleEntityInterfaces();
    // generate the map of tag data that is used by contracts
    this.generateTagData();
    this.pushVersionTwoDirectiveDefinitionsToDocumentDefinitions();
  }

  validateInterfaceImplementationsAndPushToDocumentDefinitions(
    interfaceImplementations: InterfaceImplementationData[],
  ) {
    for (const { data, clientSchemaFieldNodes } of interfaceImplementations) {
      data.node.interfaces = this.getValidImplementedInterfaces(data);
      this.routerDefinitions.push(
        getNodeForRouterSchemaByData(data, this.persistedDirectiveDefinitionByDirectiveName, this.errors),
      );
      if (isNodeDataInaccessible(data)) {
        this.validateReferencesOfInaccessibleType(data);
        this.internalGraph.setNodeInaccessible(data.name);
        continue;
      }
      const clientInterfaces: NamedTypeNode[] = [];
      for (const interfaceTypeName of data.implementedInterfaceTypeNames) {
        if (!this.inaccessibleCoords.has(interfaceTypeName)) {
          clientInterfaces.push(stringToNamedTypeNode(interfaceTypeName));
        }
      }

      /* It is not possible for clientSchemaFieldNodes to be empty.
       * If all interface fields were declared @inaccessible, the error would be caught above.
       * */
      this.clientDefinitions.push({
        ...data.node,
        directives: getClientPersistedDirectiveNodes(data),
        fields: clientSchemaFieldNodes,
        interfaces: clientInterfaces,
      });
    }
  }

  pushVersionTwoDirectiveDefinitionsToDocumentDefinitions() {
    if (!this.isVersionTwo) {
      return;
    }
    this.routerDefinitions = [
      AUTHENTICATED_DEFINITION,
      DEPRECATED_DEFINITION,
      INACCESSIBLE_DEFINITION,
      REQUIRES_SCOPES_DEFINITION,
      TAG_DEFINITION,
      SCOPE_SCALAR_DEFINITION,
    ];
    this.clientDefinitions = [
      AUTHENTICATED_DEFINITION,
      DEPRECATED_DEFINITION,
      REQUIRES_SCOPES_DEFINITION,
      SCOPE_SCALAR_DEFINITION,
    ];
  }

  validatePathSegmentInaccessibility(path: string): boolean {
    if (!path) {
      return false;
    }
    const coordinates = path.split(LEFT_PARENTHESIS)[0];
    const segments = coordinates.split(PERIOD);
    let segment = segments[0];
    for (let i = 0; i < segments.length; i++) {
      if (this.inaccessibleCoords.has(segment)) {
        return true;
      }
      segment += `.${segments[i + 1]}`;
    }
    return false;
  }

  validateReferencesOfInaccessibleType(data: ParentDefinitionData) {
    const allCoords = this.coordsByNamedTypeName.get(data.name);
    if (!allCoords || allCoords.size < 1) {
      return;
    }
    const invalidCoords: Array<string> = [];
    for (const coords of allCoords) {
      if (this.inaccessibleCoords.has(coords)) {
        continue;
      }
      if (!this.validatePathSegmentInaccessibility(coords)) {
        invalidCoords.push(coords);
      }
    }
    if (invalidCoords.length > 0) {
      this.errors.push(invalidReferencesOfInaccessibleTypeError(kindToTypeString(data.kind), data.name, invalidCoords));
    }
  }

  validateQueryRootType() {
    const query = this.parentDefinitionDataByTypeName.get(QUERY);
    if (!query || query.kind !== Kind.OBJECT_TYPE_DEFINITION || query.fieldDataByFieldName.size < 1) {
      this.errors.push(noQueryRootTypeError());
      return;
    }
    for (const fieldData of query.fieldDataByFieldName.values()) {
      if (!isNodeDataInaccessible(fieldData)) {
        return;
      }
    }
    this.errors.push(noQueryRootTypeError());
  }

  validateSubscriptionFieldConditionFieldPath(
    conditionFieldPath: string,
    objectData: ObjectDefinitionData,
    inputFieldPath: string,
    directiveSubgraphName: string,
    fieldErrorMessages: Array<string>,
  ): string[] {
    const paths = conditionFieldPath.split(PERIOD);
    if (paths.length < 1) {
      fieldErrorMessages.push(
        invalidSubscriptionFieldConditionFieldPathErrorMessage(inputFieldPath, conditionFieldPath),
      );
      return [];
    }
    let lastData: ParentDefinitionData = objectData;
    if (this.inaccessibleCoords.has(lastData.renamedTypeName)) {
      fieldErrorMessages.push(
        inaccessibleSubscriptionFieldConditionFieldPathFieldErrorMessage(
          inputFieldPath,
          conditionFieldPath,
          paths[0],
          lastData.renamedTypeName,
        ),
      );
      return [];
    }
    let partialConditionFieldPath = '';
    for (let i = 0; i < paths.length; i++) {
      const fieldName = paths[i];
      partialConditionFieldPath += partialConditionFieldPath.length > 0 ? `.${fieldName}` : fieldName;
      if (lastData.kind !== Kind.OBJECT_TYPE_DEFINITION) {
        fieldErrorMessages.push(
          invalidSubscriptionFieldConditionFieldPathParentErrorMessage(
            inputFieldPath,
            conditionFieldPath,
            partialConditionFieldPath,
          ),
        );
        return [];
      }
      const fieldData: FieldData | undefined = lastData.fieldDataByFieldName.get(fieldName);
      if (!fieldData) {
        fieldErrorMessages.push(
          undefinedSubscriptionFieldConditionFieldPathFieldErrorMessage(
            inputFieldPath,
            conditionFieldPath,
            partialConditionFieldPath,
            fieldName,
            lastData.renamedTypeName,
          ),
        );
        return [];
      }
      const fieldPath = `${lastData.renamedTypeName}.${fieldName}`;
      if (!fieldData.subgraphNames.has(directiveSubgraphName)) {
        fieldErrorMessages.push(
          invalidSubscriptionFieldConditionFieldPathFieldErrorMessage(
            inputFieldPath,
            conditionFieldPath,
            partialConditionFieldPath,
            fieldPath,
            directiveSubgraphName,
          ),
        );
        return [];
      }
      if (this.inaccessibleCoords.has(fieldPath)) {
        fieldErrorMessages.push(
          inaccessibleSubscriptionFieldConditionFieldPathFieldErrorMessage(
            inputFieldPath,
            conditionFieldPath,
            partialConditionFieldPath,
            fieldPath,
          ),
        );
        return [];
      }
      if (BASE_SCALARS.has(fieldData.namedTypeName)) {
        lastData = { kind: Kind.SCALAR_TYPE_DEFINITION, name: fieldData.namedTypeName } as ScalarDefinitionData;
        continue;
      }
      lastData = getOrThrowError(this.parentDefinitionDataByTypeName, fieldData.namedTypeName, PARENT_DEFINITION_DATA);
    }
    if (!isLeafKind(lastData.kind)) {
      fieldErrorMessages.push(
        nonLeafSubscriptionFieldConditionFieldPathFinalFieldErrorMessage(
          inputFieldPath,
          conditionFieldPath,
          paths[paths.length - 1],
          kindToTypeString(lastData.kind),
          lastData.name,
        ),
      );
      return [];
    }
    return paths;
  }

  validateSubscriptionFieldCondition(
    objectValueNode: ConstObjectValueNode,
    condition: SubscriptionFieldCondition,
    objectData: ObjectDefinitionData,
    depth: number,
    inputPath: string,
    directiveSubgraphName: string,
    errorMessages: string[],
  ): boolean {
    if (depth > MAX_SUBSCRIPTION_FILTER_DEPTH || this.isMaxDepth) {
      errorMessages.push(subscriptionFilterConditionDepthExceededErrorMessage(inputPath));
      this.isMaxDepth = true;
      return false;
    }
    let hasErrors = false;
    const validFieldNames = new Set<string>([FIELD_PATH, VALUES]);
    const duplicatedFieldNames = new Set<string>();
    const invalidFieldNames = new Set<string>();
    const fieldErrorMessages: string[] = [];
    for (const objectFieldNode of objectValueNode.fields) {
      const inputFieldName = objectFieldNode.name.value;
      const inputFieldPath = inputPath + `.${inputFieldName}`;
      switch (inputFieldName) {
        case FIELD_PATH: {
          if (validFieldNames.has(FIELD_PATH)) {
            validFieldNames.delete(FIELD_PATH);
          } else {
            hasErrors = true;
            duplicatedFieldNames.add(FIELD_PATH);
            break;
          }
          if (objectFieldNode.value.kind !== Kind.STRING) {
            fieldErrorMessages.push(
              invalidInputFieldTypeErrorMessage(inputFieldPath, STRING, kindToTypeString(objectFieldNode.value.kind)),
            );
            hasErrors = true;
            break;
          }
          const fieldPath = this.validateSubscriptionFieldConditionFieldPath(
            objectFieldNode.value.value,
            objectData,
            inputFieldPath,
            directiveSubgraphName,
            fieldErrorMessages,
          );
          if (fieldPath.length < 1) {
            hasErrors = true;
            break;
          }
          condition.fieldPath = fieldPath;
          break;
        }
        case VALUES: {
          if (validFieldNames.has(VALUES)) {
            validFieldNames.delete(VALUES);
          } else {
            hasErrors = true;
            duplicatedFieldNames.add(VALUES);
            break;
          }
          const objectFieldValueKind = objectFieldNode.value.kind;
          if (objectFieldValueKind == Kind.NULL || objectFieldValueKind == Kind.OBJECT) {
            fieldErrorMessages.push(
              invalidInputFieldTypeErrorMessage(inputFieldPath, LIST, kindToTypeString(objectFieldNode.value.kind)),
            );
            hasErrors = true;
            break;
          }
          // Coerce scalars into a list
          if (objectFieldValueKind !== Kind.LIST) {
            condition.values = [getSubscriptionFilterValue(objectFieldNode.value)];
            break;
          }
          // Prevent duplicate values
          const values = new Set<SubscriptionFilterValue>();
          const invalidIndices: number[] = [];
          for (let i = 0; i < objectFieldNode.value.values.length; i++) {
            const valueNode = objectFieldNode.value.values[i];
            if (valueNode.kind === Kind.OBJECT || valueNode.kind === Kind.LIST) {