Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 31 additions & 31 deletions composition-go/index.global.js

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions composition/src/errors/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { getEntriesNotInHashSet, getOrThrowError, kindToNodeType, numberToOrdina
import { ImplementationErrors, InvalidEntityInterface, InvalidRequiredInputValueData } from '../utils/types';
import { isFieldData } from '../schema-building/utils';
import { printTypeNode } from '@graphql-tools/merge';
import { NodeType, TypeName } from '../types/types';

export const minimumSubgraphRequirementError = new Error('At least one subgraph is required for federation.');

Expand Down Expand Up @@ -424,9 +425,9 @@ export function subgraphInvalidSyntaxError(error?: Error): Error {
}

export function invalidInterfaceImplementationError(
parentTypeName: string,
parentTypeString: string,
implementationErrorsByInterfaceTypeName: Map<string, ImplementationErrors>,
parentTypeName: TypeName,
parentNodeType: NodeType,
implementationErrorsByInterfaceTypeName: Map<TypeName, ImplementationErrors>,
): Error {
const messages: string[] = [];
for (const [interfaceName, implementationErrors] of implementationErrorsByInterfaceTypeName) {
Expand Down Expand Up @@ -486,7 +487,7 @@ export function invalidInterfaceImplementationError(
messages.push(message);
}
return new Error(
`The ${parentTypeString} "${parentTypeName}" has the following Interface implementation errors:\n` +
`The ${parentNodeType} "${parentTypeName}" has the following Interface implementation errors:\n` +
messages.join('\n'),
);
}
Expand Down Expand Up @@ -856,8 +857,8 @@ export function undefinedEntityInterfaceImplementationsError(
);
const implementedConcreteTypeNames = entityInterfaceDatas.concreteTypeNames!;
message +=
` Across all subgraphs, the entity interface "${typeName}" is implemented by the following entities` +
(implementedConcreteTypeNames.size > 1 ? `s` : ``) +
` Across all subgraphs, the entity interface "${typeName}" is implemented by the following entit` +
(implementedConcreteTypeNames.size > 1 ? `ies` : `y`) +
`:\n "` +
Array.from(implementedConcreteTypeNames).join(QUOTATION_JOIN) +
`"\n` +
Expand Down
7 changes: 4 additions & 3 deletions composition/src/schema-building/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
import { FieldSetConditionData } from '../router-configuration/types';
import { KeyFieldSetData } from '../v1/normalization/types';
import { InputNodeKind, OutputNodeKind } from '../utils/types';
import { FieldName, SubgraphName } from '../types/types';

export type ArgumentData = {
name: string;
Expand Down Expand Up @@ -101,7 +102,7 @@ export type FieldData = {
federatedCoords: string;
inheritedDirectiveNames: Set<string>;
isInaccessible: boolean;
isShareableBySubgraphName: Map<string, boolean>;
isShareableBySubgraphName: Map<SubgraphName, boolean>;
kind: Kind.FIELD_DEFINITION;
name: string;
namedTypeKind: OutputNodeKind | Kind.NULL;
Expand All @@ -110,7 +111,7 @@ export type FieldData = {
originalParentTypeName: string;
persistedDirectivesData: PersistedDirectivesData;
renamedParentTypeName: string;
subgraphNames: Set<string>;
subgraphNames: Set<SubgraphName>;
type: MutableTypeNode;
description?: StringValueNode;
};
Expand Down Expand Up @@ -172,7 +173,7 @@ export type ObjectDefinitionData = {
configureDescriptionDataBySubgraphName: Map<string, ConfigureDescriptionData>;
directivesByDirectiveName: Map<string, Array<ConstDirectiveNode>>;
extensionType: ExtensionType;
fieldDataByName: Map<string, FieldData>;
fieldDataByName: Map<FieldName, FieldData>;
implementedInterfaceTypeNames: Set<string>;
isEntity: boolean;
isInaccessible: boolean;
Expand Down
3 changes: 2 additions & 1 deletion composition/src/schema-building/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
import { generateRequiresScopesDirective, generateSimpleDirective, getEntriesNotInHashSet } from '../utils/utils';
import { InputNodeKind, InvalidRequiredInputValueData, OutputNodeKind } from '../utils/types';
import { getDescriptionFromString } from '../v1/federation/utils';
import { SubgraphName } from '../types/types';

export function newPersistedDirectivesData(): PersistedDirectivesData {
return {
Expand Down Expand Up @@ -572,7 +573,7 @@ export function newInvalidFieldNames() {
export function validateExternalAndShareable(fieldData: FieldData, invalidFieldNames: InvalidFieldNames) {
// fieldData.subgraphNames.size is not used due to overridden fields
const instances = fieldData.isShareableBySubgraphName.size;
let externalFieldSubgraphNames: Array<string> = [];
let externalFieldSubgraphNames = new Array<SubgraphName>();
let unshareableFields = 0;
for (const [subgraphName, isShareable] of fieldData.isShareableBySubgraphName) {
/*
Expand Down
10 changes: 10 additions & 0 deletions composition/src/types/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
export type ContractName = string;

export type FieldName = string;

export type FieldCoords = string;

export type SubgraphName = string;

export type TypeName = string;

export type NodeType = string;
55 changes: 31 additions & 24 deletions composition/src/v1/federation/federation-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ import {
newAuthorizationData,
newEntityInterfaceFederationData,
newFieldAuthorizationData,
subtractSet,
upsertAuthorizationConfiguration,
upsertEntityInterfaceFederationData,
upsertFieldAuthorizationData,
Expand Down Expand Up @@ -231,7 +230,7 @@ import {
InvalidRequiredInputValueData,
} from '../../utils/types';
import { FederateSubgraphsContractV1Params, FederateSubgraphsWithContractsV1Params, FederationParams } from './types';
import { ContractName } from '../../types/types';
import { ContractName, FieldCoords, TypeName } from '../../types/types';

export class FederationFactory {
authorizationDataByParentTypeName: Map<string, AuthorizationData>;
Expand All @@ -245,7 +244,7 @@ export class FederationFactory {
entityInterfaceFederationDataByTypeName: Map<string, EntityInterfaceFederationData>;
errors: Error[] = [];
fieldConfigurationByFieldCoords = new Map<string, FieldConfiguration>();
fieldCoordsByNamedTypeName: Map<string, Set<string>>;
fieldCoordsByNamedTypeName: Map<TypeName, Set<FieldCoords>>;
inaccessibleCoords = new Set<string>();
inaccessibleRequiredInputValueErrorByCoords = new Map<string, Error>();
internalGraph: Graph;
Expand Down Expand Up @@ -425,11 +424,11 @@ export class FederationFactory {
objectData?.kind || Kind.NULL,
);
}
const configurationData = getOrThrowError(
internalSubgraph.configurationDataByTypeName,
entityData.typeName,
'internalSubgraph.configurationDataByTypeName',
);
const configurationData = internalSubgraph.configurationDataByTypeName.get(entityData.typeName);
// If all fields are overridden, there will be no configuration data.
if (!configurationData) {
return;
}
const implicitKeys: RequiredFieldConfiguration[] = [];
const graphNode = this.internalGraph.nodeByNodeName.get(`${this.currentSubgraphName}.${entityData.typeName}`);
// Any errors in the field sets would be caught when evaluating the explicit entities, so they are ignored here
Expand Down Expand Up @@ -1574,13 +1573,12 @@ export class FederationFactory {

handleEntityInterfaces() {
for (const [entityInterfaceTypeName, entityInterfaceData] of this.entityInterfaceFederationDataByTypeName) {
subtractSet(entityInterfaceData.interfaceFieldNames, entityInterfaceData.interfaceObjectFieldNames);
const entityInterface = getOrThrowError(
const entityInterfaceFederationData = getOrThrowError(
this.parentDefinitionDataByTypeName,
entityInterfaceTypeName,
PARENT_DEFINITION_DATA,
);
if (entityInterface.kind !== Kind.INTERFACE_TYPE_DEFINITION) {
if (entityInterfaceFederationData.kind !== Kind.INTERFACE_TYPE_DEFINITION) {
// TODO error
continue;
}
Expand All @@ -1605,7 +1603,9 @@ export class FederationFactory {
// TODO no keys error
continue;
}
interfaceObjectConfiguration.entityInterfaceConcreteTypeNames = entityInterfaceData.concreteTypeNames;
interfaceObjectConfiguration.entityInterfaceConcreteTypeNames = new Set<TypeName>(
entityInterfaceData.concreteTypeNames,
);
this.internalGraph.setSubgraphName(subgraphName);
const interfaceObjectNode = this.internalGraph.addOrUpdateNode(entityInterfaceTypeName, { isAbstract: true });
for (const concreteTypeName of concreteTypeNames) {
Expand Down Expand Up @@ -1648,17 +1648,20 @@ export class FederationFactory {
resolvableKeyFieldSets.add(key.selectionSet);
}
const interfaceAuthData = this.authorizationDataByParentTypeName.get(entityInterfaceTypeName);
for (const fieldName of entityInterfaceData.interfaceObjectFieldNames) {
const entityInterfaceSubgraphData = getOrThrowError(
internalSubgraph.parentDefinitionDataByTypeName,
entityInterfaceTypeName,
'internalSubgraph.parentDefinitionDataByTypeName',
);
if (!isObjectDefinitionData(entityInterfaceSubgraphData)) {
continue;
}
for (const [fieldName, fieldData] of entityInterfaceSubgraphData.fieldDataByName) {
const fieldCoords = `${concreteTypeName}.${fieldName}`;
const interfaceFieldData = getOrThrowError(
entityInterface.fieldDataByName,
fieldName,
`${entityInterfaceTypeName}.fieldDataByFieldName`,
);
getValueOrDefault(
this.fieldCoordsByNamedTypeName,
interfaceFieldData.namedTypeName,
() => new Set<string>(),
fieldData.namedTypeName,
() => new Set<FieldCoords>(),
).add(fieldCoords);
const interfaceFieldAuthData = interfaceAuthData?.fieldAuthDataByFieldName.get(fieldName);
if (interfaceFieldAuthData) {
Expand All @@ -1671,12 +1674,16 @@ export class FederationFactory {
}
const existingFieldData = concreteTypeData.fieldDataByName.get(fieldName);
if (existingFieldData) {
// TODO handle shareability
const isShareable = fieldData.isShareableBySubgraphName.get(subgraphName) ?? false;
existingFieldData.isShareableBySubgraphName.set(subgraphName, isShareable);
existingFieldData.subgraphNames.add(subgraphName);
continue;
}
const isInaccessible =
entityInterface.isInaccessible || concreteTypeData.isInaccessible || interfaceFieldData.isInaccessible;
concreteTypeData.fieldDataByName.set(fieldName, this.copyFieldData(interfaceFieldData, isInaccessible));
entityInterfaceFederationData.isInaccessible ||
concreteTypeData.isInaccessible ||
fieldData.isInaccessible;
concreteTypeData.fieldDataByName.set(fieldName, this.copyFieldData(fieldData, isInaccessible));
}
this.handleInterfaceObjectForInternalGraph({
internalSubgraph,
Expand Down Expand Up @@ -3053,7 +3060,7 @@ function initializeFederationFactory({
upsertEntityInterfaceFederationData(existingData, entityInterfaceData, subgraphName);
}
}
const entityInterfaceErrors: Array<Error> = [];
const entityInterfaceErrors = new Array<Error>();
for (const [typeName, entityInterfaceData] of entityInterfaceFederationDataByTypeName) {
const implementations = entityInterfaceData.concreteTypeNames.size;
for (const [subgraphName, subgraphData] of entityInterfaceData.subgraphDataByTypeName) {
Expand Down
9 changes: 7 additions & 2 deletions composition/src/v1/normalization/normalization-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ import {
INHERITABLE_DIRECTIVE_NAMES,
INPUT_FIELD,
INT_SCALAR,
INTERFACE_OBJECT,
KEY,
LINK,
LINK_IMPORT,
Expand Down Expand Up @@ -1267,11 +1268,15 @@ export class NormalizationFactory {
return;
}
this.updateCompositeOutputDataByNode(node, parentData, extensionType);
this.addConcreteTypeNamesForImplementedInterfaces(parentData.implementedInterfaceTypeNames, typeName);
if (!directivesByDirectiveName.has(INTERFACE_OBJECT)) {
this.addConcreteTypeNamesForImplementedInterfaces(parentData.implementedInterfaceTypeNames, typeName);
}
return;
}
const implementedInterfaceTypeNames = this.extractImplementedInterfaceTypeNames(node, new Set<string>());
this.addConcreteTypeNamesForImplementedInterfaces(implementedInterfaceTypeNames, typeName);
if (!directivesByDirectiveName.has(INTERFACE_OBJECT)) {
this.addConcreteTypeNamesForImplementedInterfaces(implementedInterfaceTypeNames, typeName);
}
const newParentData: ObjectDefinitionData = {
configureDescriptionDataBySubgraphName: new Map<string, ConfigureDescriptionData>(),
directivesByDirectiveName,
Expand Down
7 changes: 6 additions & 1 deletion composition/tests/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
federateSubgraphsContract,
federateSubgraphsWithContracts,
FederationFailure,
FederationSuccess,
FederationResultWithContractsSuccess,
FederationSuccess,
NormalizationFailure,
NormalizationSuccess,
normalizeSubgraph,
Expand Down Expand Up @@ -62,6 +62,11 @@ export function federateSubgraphsSuccess(
disableResolvabilityValidation = false,
): FederationSuccess {
const result = federateSubgraphs({ disableResolvabilityValidation, subgraphs, version });
if (!result.success) {
for (const error of result.errors) {
console.dir(error, { depth: null });
}
}
expect(result.success, 'federateSubgraphs failed when expected to succeed').toBe(true);
return result as FederationSuccess;
}
Expand Down
Loading
Loading