Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: refactor normalization factory #528

Merged
merged 7 commits into from
Feb 19, 2024
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
483 changes: 182 additions & 301 deletions composition-go/index.global.js

Large diffs are not rendered by default.

41 changes: 3 additions & 38 deletions composition/src/ast/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import {
TypeNode,
UnionTypeDefinitionNode,
} from 'graphql';
import { federationUnexpectedNodeKindError } from '../errors/errors';
import { formatDescription } from './utils';
import { deepCopyTypeNode } from '../schema-building/ast';

function deepCopyFieldsAndInterfaces(
node: InterfaceTypeDefinitionNode | ObjectTypeDefinitionNode | ObjectTypeExtensionNode,
Expand All @@ -50,33 +50,6 @@ export type ConstValueNodeWithValue =
| BooleanValueNode
| EnumValueNode;

export function deepCopyTypeNode(node: TypeNode, parentName: string, fieldName: string): TypeNode {
const deepCopy: MutableTypeNode = { kind: node.kind };
let lastTypeNode = deepCopy;
for (let i = 0; i < maximumTypeNesting; i++) {
switch (node.kind) {
case Kind.NAMED_TYPE:
lastTypeNode.name = { ...node.name };
return deepCopy as TypeNode;
case Kind.LIST_TYPE:
lastTypeNode.kind = node.kind;
lastTypeNode.type = { kind: node.type.kind };
lastTypeNode = lastTypeNode.type;
node = node.type;
continue;
case Kind.NON_NULL_TYPE:
lastTypeNode.kind = node.kind;
lastTypeNode.type = { kind: node.type.kind };
lastTypeNode = lastTypeNode.type;
node = node.type;
continue;
default:
throw federationUnexpectedNodeKindError(parentName, fieldName);
}
}
throw new Error(`Field ${parentName}.${fieldName} has more than 30 layers of nesting, or there is a cyclical error.`);
}

export type MutableDirectiveDefinitionNode = {
arguments?: InputValueDefinitionNode[];
description?: StringValueNode;
Expand Down Expand Up @@ -258,12 +231,12 @@ export function objectTypeDefinitionNodeToMutable(node: ObjectTypeDefinitionNode
}

export type MutableObjectTypeExtensionNode = {
description?: StringValueNode;
directives?: ConstDirectiveNode[];
fields: FieldDefinitionNode[];
interfaces: NamedTypeNode[];
kind: Kind.OBJECT_TYPE_EXTENSION;
name: NameNode;
description?: StringValueNode;
directives?: ConstDirectiveNode[];
};

export function objectTypeExtensionNodeToMutable(node: ObjectTypeExtensionNode): MutableObjectTypeExtensionNode {
Expand Down Expand Up @@ -307,14 +280,6 @@ export function scalarTypeDefinitionNodeToMutable(node: ScalarTypeDefinitionNode
};
}

export type MutableTypeNode = {
kind: Kind.NAMED_TYPE | Kind.LIST_TYPE | Kind.NON_NULL_TYPE;
name?: NameNode;
type?: MutableTypeNode;
};

export const maximumTypeNesting = 30;

export type MutableUnionTypeDefinitionNode = {
description?: StringValueNode;
directives?: ConstDirectiveNode[];
Expand Down
12 changes: 5 additions & 7 deletions composition/src/ast/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
ArgumentNode,
ConstDirectiveNode,
DocumentNode,
EnumTypeDefinitionNode,
EnumTypeExtensionNode,
FieldDefinitionNode,
FieldNode,
InputObjectTypeDefinitionNode,
Expand All @@ -15,10 +17,13 @@ import {
ObjectTypeExtensionNode,
OperationTypeNode,
parse,
ScalarTypeDefinitionNode,
ScalarTypeExtensionNode,
SchemaDefinitionNode,
SchemaExtensionNode,
SelectionSetNode,
StringValueNode,
UnionTypeDefinitionNode,
UnionTypeExtensionNode,
} from 'graphql';
import {
Expand Down Expand Up @@ -54,13 +59,6 @@ import {
UNION_UPPER,
} from '../utils/string-constants';
import { duplicateInterfaceError, unexpectedKindFatalError } from '../errors/errors';
import {
EnumTypeDefinitionNode,
EnumTypeExtensionNode,
ScalarTypeDefinitionNode,
ScalarTypeExtensionNode,
UnionTypeDefinitionNode,
} from 'graphql/index';
import { DirectiveContainer, EXECUTABLE_DIRECTIVE_LOCATIONS, NodeContainer } from '../federation/utils';

export function isObjectLikeNodeEntity(node: ObjectLikeTypeNode): boolean {
Expand Down
49 changes: 15 additions & 34 deletions composition/src/errors/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export function duplicateInterfaceError(interfaceName: string, typeName: string)
return new Error(`Interface "${interfaceName}" can only be defined on type "${typeName}" once.`);
}

export function duplicateUnionMemberError(memberName: string, typeName: string): Error {
export function duplicateUnionMemberExtensionError(memberName: string, typeName: string): Error {
return new Error(`Extension error:\n Member "${memberName}" already exists on union "${typeName}".`);
}

Expand Down Expand Up @@ -163,8 +163,10 @@ export function noBaseTypeExtensionError(typeName: string): Error {
return new Error(`Extension error:\n Could not extend the type "${typeName}" because no base definition exists.`);
}

export function noDefinedUnionMembersError(unionName: string): Error {
return new Error(`The union "${unionName}" must define at least one union member.`);
export function noDefinedUnionMembersError(unionTypeName: string, extension = false): Error {
return new Error(
`The union ` + (extension ? 'extension' : '') + ` "${unionTypeName}" must define at least one union member.`,
);
}

export function operationDefinitionError(typeName: string, operationType: OperationTypeNode, actualType: Kind): Error {
Expand Down Expand Up @@ -211,10 +213,10 @@ export function shareableFieldDefinitionsError(parent: ObjectContainer, children
);
}

export function undefinedDirectiveError(directiveName: string, hostPath: string): Error {
return new Error(
export function undefinedDirectiveErrorMessage(directiveName: string, hostPath: string): string {
return (
`The directive "${directiveName}" is declared on "${hostPath}",` +
` but the directive is not defined in the schema.`,
` but the directive is not defined in the schema.`
);
}

Expand Down Expand Up @@ -288,6 +290,10 @@ export function invalidUnionError(unionName: string): Error {
return new Error(`Union "${unionName}" must have at least one member.`);
}

export function duplicateUnionMemberError(memberTypeName: string, unionTypeName: string): Error {
return new Error(`Member "${memberTypeName}" must only be defined on union "${unionTypeName}" once.`);
}

export const invalidDeprecatedDirectiveError = new Error(`
Expected the @deprecated directive to have a single optional argument "reason" of the type "String!"
`);
Expand Down Expand Up @@ -373,29 +379,6 @@ export function invalidKeyDirectiveArgumentErrorMessage(directiveKind: Kind): st
return ` The required argument named "fields" must be type "String" and not type "${directiveKind}".`;
}

export function invalidGraphQLNameErrorMessage(type: string, name: string): string {
return (
` The ${type} "${name}" is an invalid GraphQL name:\n` +
` GraphQL names must match the following regex: /[_a-zA-Z][_a-zA-Z0-9]*/`
);
}

export const invalidOpeningBraceErrorMessage: string = ` Unexpected brace opening:\n Received an opening brace "{" before the parent value was defined.`;

export const invalidClosingBraceErrorMessage: string = ` Unexpected brace closure:\n Received a closing brace "}" before any nested values were defined.`;

export const invalidNestingClosureErrorMessage: string = ` Unexpected brace closure:\n Received a closing brace "}" before its corresponding opening brace "{" was defined.`;

export const invalidNestingErrorMessage: string = ` Invalid nesting:\n A nested key was terminated without a closing brace "}".`;

export function invalidEntityKeyError(parentTypeName: string, entityKey: string, errorMessage: string): Error {
return new Error(
`The directive "key" declared on the object "${parentTypeName}"` +
` with the "fields" argument value of "${entityKey}" is invalid for the following reason:\n` +
errorMessage,
);
}

export function invalidKeyDirectivesError(parentTypeName: string, errorMessages: string[]): Error {
return new Error(
`The entity "${parentTypeName}" defines the following invalid "key" directive` +
Expand Down Expand Up @@ -438,7 +421,7 @@ export function unexpectedDirectiveLocationError(locationName: string): Error {
return new Error(`Fatal: Unknown directive location "${locationName}".`);
}

export function unexpectedTypeNodeKindError(childPath: string): Error {
export function unexpectedTypeNodeKindFatalError(childPath: string): Error {
return new Error(
`Fatal: Expected all constituent types of "${childPath}" to be one of the following: ` +
`"LIST_TYPE", "NAMED_TYPE", or "NON_NULL_TYPE".`,
Expand Down Expand Up @@ -881,10 +864,8 @@ export function allFieldDefinitionsAreInaccessibleError(typeString: string, type
);
}

export function equivalentSourceAndTargetOverrideError(subgraphName: string, hostPath: string): Error {
return new Error(
`Cannot override field "${hostPath}" because the source and target subgraph names are both "${subgraphName}"`,
);
export function equivalentSourceAndTargetOverrideErrorMessage(subgraphName: string, hostPath: string): string {
return `Cannot override field "${hostPath}" because the source and target subgraph names are both "${subgraphName}"`;
}

export function undefinedEntityInterfaceImplementationsError(
Expand Down
Loading
Loading