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
286 changes: 143 additions & 143 deletions composition-go/index.global.js

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions composition/src/ast/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,14 @@ export type ScalarTypeNode = ScalarTypeDefinitionNode | ScalarTypeExtensionNode;
export type SchemaNode = SchemaDefinitionNode | SchemaExtensionNode;
export type UnionTypeNode = UnionTypeDefinitionNode | UnionTypeExtensionNode;

export type ParentTypeNode =
| EnumTypeNode
| InputObjectTypeNode
| InterfaceTypeNode
| ObjectTypeNode
| ScalarTypeNode
| UnionTypeNode;

export type InterfaceNodeKind = Kind.INTERFACE_TYPE_DEFINITION | Kind.INTERFACE_TYPE_EXTENSION;
export type ObjectNodeKind = Kind.OBJECT_TYPE_DEFINITION | Kind.OBJECT_TYPE_EXTENSION;
export type CompositeOutputNodeKind = InterfaceNodeKind | ObjectNodeKind;
22 changes: 15 additions & 7 deletions composition/src/normalization/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ import {
} from '../schema-building/types';
import { type Graph } from '../resolvability-graph/graph';
import { type InternalSubgraph } from '../subgraph/types';
import { type DirectiveName, type TypeName } from '../types/types';
import {
type AbstractTypeName,
type DirectiveName,
type InterfaceTypeName,
type SubgraphName,
type TypeName,
} from '../types/types';

export type NormalizationFailure = {
errors: Array<Error>;
Expand All @@ -36,9 +42,10 @@ export type NormalizationSuccess = {
entityInterfaces: Map<string, EntityInterfaceSubgraphData>;
entityDataByTypeName: Map<string, EntityData>;
fieldCoordsByNamedTypeName: Map<string, Set<string>>;
originalTypeNameByRenamedTypeName: Map<string, string>;
interfaceImplementationTypeNamesByInterfaceTypeName: Map<InterfaceTypeName, Set<InterfaceTypeName>>;
isEventDrivenGraph: boolean;
isVersionTwo: boolean;
originalTypeNameByRenamedTypeName: Map<string, string>;
keyFieldNamesByParentTypeName: Map<string, Set<string>>;
keyFieldSetsByEntityTypeNameByKeyFieldCoords: Map<string, Map<string, Set<string>>>;
operationTypes: Map<string, OperationTypeNode>;
Expand All @@ -63,11 +70,12 @@ export type BatchNormalizationFailure = {

export type BatchNormalizationSuccess = {
success: true;
authorizationDataByParentTypeName: Map<string, AuthorizationData>;
concreteTypeNamesByAbstractTypeName: Map<string, Set<string>>;
entityDataByTypeName: Map<string, EntityData>;
fieldCoordsByNamedTypeName: Map<string, Set<string>>;
internalSubgraphBySubgraphName: Map<string, InternalSubgraph>;
authorizationDataByParentTypeName: Map<TypeName, AuthorizationData>;
concreteTypeNamesByAbstractTypeName: Map<AbstractTypeName, Set<TypeName>>;
entityDataByTypeName: Map<TypeName, EntityData>;
fieldCoordsByNamedTypeName: Map<TypeName, Set<string>>;
interfaceImplementationTypeNamesByInterfaceTypeName: Map<InterfaceTypeName, Set<InterfaceTypeName>>;
internalSubgraphBySubgraphName: Map<SubgraphName, InternalSubgraph>;
internalGraph: Graph;
warnings: Array<Warning>;
};
Expand Down
9 changes: 9 additions & 0 deletions composition/src/schema-building/params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { type TypeNode } from 'graphql';
import { type InterfaceTypeName, type TypeName } from '../types/types';

export type IsTypeValidImplementationParams = {
concreteTypeNamesByAbstractTypeName: Map<TypeName, Set<TypeName>>;
implementationType: TypeNode;
interfaceImplementationTypeNamesByInterfaceTypeName: Map<InterfaceTypeName, Set<InterfaceTypeName>>;
originalType: TypeNode;
};
54 changes: 38 additions & 16 deletions composition/src/schema-building/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ import {
type SchemaData,
} from './types';
import { type MutableDefinitionNode, type MutableFieldNode, type MutableInputValueNode } from './ast';
import { type ObjectTypeNode, setToNameNodeArray, stringToNameNode } from '../ast/utils';
import {
type InterfaceTypeNode,
type ObjectTypeNode,
type ParentTypeNode,
setToNameNodeArray,
stringToNameNode,
} from '../ast/utils';
import {
incompatibleInputValueDefaultValuesError,
invalidRepeatedFederatedDirectiveErrorMessage,
Expand All @@ -58,6 +64,7 @@ import {
INPUT_FIELD,
INPUT_NODE_KINDS,
INT_SCALAR,
INTERFACE_NODE_KINDS,
MUTATION,
OUTPUT_NODE_KINDS,
PERSISTED_CLIENT_DIRECTIVES,
Expand All @@ -80,6 +87,7 @@ import {
import { type InputNodeKind, type InvalidRequiredInputValueData, type OutputNodeKind } from '../utils/types';
import { getDescriptionFromString } from '../v1/federation/utils';
import { type DirectiveName, type FieldName, type SubgraphName, type TypeName } from '../types/types';
import { type IsTypeValidImplementationParams } from './params';

export function newPersistedDirectivesData(): PersistedDirectivesData {
return {
Expand Down Expand Up @@ -620,19 +628,30 @@ export enum MergeMethod {
CONSISTENT,
}

export function isTypeValidImplementation(
originalType: TypeNode,
implementationType: TypeNode,
concreteTypeNamesByAbstractTypeName: Map<TypeName, Set<TypeName>>,
): boolean {
export function isTypeValidImplementation({
concreteTypeNamesByAbstractTypeName,
implementationType,
interfaceImplementationTypeNamesByInterfaceTypeName,
originalType,
}: IsTypeValidImplementationParams): boolean {
if (originalType.kind === Kind.NON_NULL_TYPE) {
if (implementationType.kind !== Kind.NON_NULL_TYPE) {
return false;
}
return isTypeValidImplementation(originalType.type, implementationType.type, concreteTypeNamesByAbstractTypeName);
return isTypeValidImplementation({
concreteTypeNamesByAbstractTypeName,
implementationType: implementationType.type,
interfaceImplementationTypeNamesByInterfaceTypeName,
originalType: originalType.type,
});
}
if (implementationType.kind === Kind.NON_NULL_TYPE) {
return isTypeValidImplementation(originalType, implementationType.type, concreteTypeNamesByAbstractTypeName);
return isTypeValidImplementation({
concreteTypeNamesByAbstractTypeName,
implementationType: implementationType.type,
interfaceImplementationTypeNamesByInterfaceTypeName,
originalType,
});
}
switch (originalType.kind) {
case Kind.NAMED_TYPE:
Expand All @@ -642,20 +661,19 @@ export function isTypeValidImplementation(
if (originalTypeName === implementationTypeName) {
return true;
}
const abstractTypes = interfaceImplementationTypeNamesByInterfaceTypeName.get(originalTypeName);
const concreteTypes = concreteTypeNamesByAbstractTypeName.get(originalTypeName);
if (!concreteTypes) {
return false;
}
return concreteTypes.has(implementationTypeName);
return !!(concreteTypes?.has(implementationTypeName) || abstractTypes?.has(implementationTypeName));
}
return false;
default:
if (implementationType.kind === Kind.LIST_TYPE) {
return isTypeValidImplementation(
originalType.type,
implementationType.type,
return isTypeValidImplementation({
concreteTypeNamesByAbstractTypeName,
);
implementationType: implementationType.type,
interfaceImplementationTypeNamesByInterfaceTypeName,
originalType: originalType.type,
});
}
return false;
}
Expand Down Expand Up @@ -775,3 +793,7 @@ export function isInputNodeKind(kind: Kind): kind is InputNodeKind {
export function isOutputNodeKind(kind: Kind): kind is OutputNodeKind {
return OUTPUT_NODE_KINDS.has(kind);
}

export function isInterfaceNode(node: ParentTypeNode): node is InterfaceTypeNode {
return INTERFACE_NODE_KINDS.has(node.kind);
}
4 changes: 4 additions & 0 deletions composition/src/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export type AbstractTypeName = TypeName;

export type ArgumentName = string;

export type ContractName = string;
Expand All @@ -11,6 +13,8 @@ export type FieldName = string;

export type FieldCoords = string;

export type InterfaceTypeName = string;

export type SubgraphName = string;

export type TypeName = string;
Expand Down
2 changes: 2 additions & 0 deletions composition/src/utils/string-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,6 @@ export const OUTPUT_NODE_KINDS = new Set<Kind>([
Kind.UNION_TYPE_DEFINITION,
]);

export const INTERFACE_NODE_KINDS = new Set<Kind>([Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION]);

export const NON_REPEATABLE_PERSISTED_DIRECTIVES = new Set<DirectiveName>([INACCESSIBLE, ONE_OF, SEMANTIC_NON_NULL]);
4 changes: 2 additions & 2 deletions composition/src/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type Kind } from 'graphql';
import { type SubgraphName } from '../types/types';
import { type ArgumentName, type SubgraphName } from '../types/types';

export type RootTypeName = 'Mutation' | 'Query' | 'Subscription';

Expand All @@ -15,7 +15,7 @@ export type InvalidFieldImplementation = {
invalidImplementedArguments: InvalidArgumentImplementation[];
isInaccessible: boolean;
originalResponseType: string;
unimplementedArguments: Set<string>;
unimplementedArguments: Set<ArgumentName>;
};

export type ImplementationErrors = {
Expand Down
20 changes: 14 additions & 6 deletions composition/src/v1/federation/federation-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,12 @@ import type {
InvalidRequiredInputValueData,
} from '../../utils/types';
import {
type ArgumentName,
type ContractName,
type DirectiveName,
type FieldCoords,
type FieldName,
type InterfaceTypeName,
type SubgraphName,
type TypeName,
} from '../../types/types';
Expand Down Expand Up @@ -279,6 +281,7 @@ export class FederationFactory {
fieldCoordsByNamedTypeName: Map<TypeName, Set<FieldCoords>>;
inaccessibleCoords = new Set<string>();
inaccessibleRequiredInputValueErrorByCoords = new Map<string, Error>();
interfaceImplementationTypeNamesByInterfaceTypeName: Map<InterfaceTypeName, Set<InterfaceTypeName>>;
internalGraph: Graph;
internalSubgraphBySubgraphName: Map<SubgraphName, InternalSubgraph>;
invalidORScopesCoords = new Set<string>();
Expand Down Expand Up @@ -311,6 +314,7 @@ export class FederationFactory {
entityDataByTypeName,
entityInterfaceFederationDataByTypeName,
fieldCoordsByNamedTypeName,
interfaceImplementationTypeNamesByInterfaceTypeName,
internalGraph,
internalSubgraphBySubgraphName,
options,
Expand All @@ -322,6 +326,7 @@ export class FederationFactory {
this.entityDataByTypeName = entityDataByTypeName;
this.entityInterfaceFederationDataByTypeName = entityInterfaceFederationDataByTypeName;
this.fieldCoordsByNamedTypeName = fieldCoordsByNamedTypeName;
this.interfaceImplementationTypeNamesByInterfaceTypeName = interfaceImplementationTypeNamesByInterfaceTypeName;
this.internalGraph = internalGraph;
this.internalSubgraphBySubgraphName = internalSubgraphBySubgraphName;
this.warnings = warnings;
Expand Down Expand Up @@ -402,15 +407,17 @@ export class FederationFactory {
invalidImplementedArguments: [],
isInaccessible: false,
originalResponseType: printTypeNode(interfaceField.node.type),
unimplementedArguments: new Set<string>(),
unimplementedArguments: new Set<ArgumentName>(),
};
// The implemented field type must be equally or more restrictive than the original interface field type
if (
!isTypeValidImplementation(
interfaceField.node.type,
fieldData.node.type,
this.concreteTypeNamesByAbstractTypeName,
)
!isTypeValidImplementation({
concreteTypeNamesByAbstractTypeName: this.concreteTypeNamesByAbstractTypeName,
implementationType: fieldData.node.type,
interfaceImplementationTypeNamesByInterfaceTypeName:
this.interfaceImplementationTypeNamesByInterfaceTypeName,
originalType: interfaceField.node.type,
})
) {
hasErrors = true;
hasNestedErrors = true;
Expand Down Expand Up @@ -3373,6 +3380,7 @@ function initializeFederationFactory({ options, subgraphs }: FederationParams):
entityDataByTypeName: result.entityDataByTypeName,
entityInterfaceFederationDataByTypeName,
fieldCoordsByNamedTypeName: result.fieldCoordsByNamedTypeName,
interfaceImplementationTypeNamesByInterfaceTypeName: result.interfaceImplementationTypeNamesByInterfaceTypeName,
internalSubgraphBySubgraphName: result.internalSubgraphBySubgraphName,
internalGraph: result.internalGraph,
options,
Expand Down
10 changes: 9 additions & 1 deletion composition/src/v1/federation/params.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import type { ContractName, DirectiveName, FieldName, SubgraphName, TypeName } from '../../types/types';
import type {
ContractName,
DirectiveName,
FieldName,
InterfaceTypeName,
SubgraphName,
TypeName,
} from '../../types/types';
import type {
AuthorizationData,
EntityData,
Expand Down Expand Up @@ -47,6 +54,7 @@ export type FederationFactoryParams = {
entityDataByTypeName: Map<TypeName, EntityData>;
entityInterfaceFederationDataByTypeName: Map<TypeName, EntityInterfaceFederationData>;
fieldCoordsByNamedTypeName: Map<TypeName, Set<string>>;
interfaceImplementationTypeNamesByInterfaceTypeName: Map<InterfaceTypeName, Set<InterfaceTypeName>>;
internalGraph: Graph;
internalSubgraphBySubgraphName: Map<SubgraphName, InternalSubgraph>;
warnings: Array<Warning>;
Expand Down
Loading
Loading