Skip to content

Commit

Permalink
Fix circular dependencies (#1326)
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilkisiela committed Apr 1, 2020
1 parent 29aa215 commit d8c43b4
Show file tree
Hide file tree
Showing 20 changed files with 367 additions and 323 deletions.
245 changes: 5 additions & 240 deletions src/delegate/checkResultAndHandleErrors.ts
Expand Up @@ -6,34 +6,17 @@ import {
isLeafType,
isListType,
ExecutionResult,
GraphQLCompositeType,
GraphQLError,
GraphQLList,
GraphQLOutputType,
GraphQLType,
GraphQLSchema,
FieldNode,
isAbstractType,
GraphQLObjectType,
} from 'graphql';
import { collectFields, ExecutionContext } from 'graphql/execution/execute';

import {
SubschemaConfig,
IGraphQLToolsResolveInfo,
isSubschemaConfig,
MergedTypeInfo,
} from '../Interfaces';

import {
relocatedError,
combineErrors,
getErrorsByPathSegment,
} from '../stitch/errors';
import { SubschemaConfig, IGraphQLToolsResolveInfo } from '../Interfaces';
import { getResponseKeyFromInfo } from '../stitch/getResponseKeyFromInfo';
import resolveFromParentTypename from '../stitch/resolveFromParentTypename';
import { setErrors, setObjectSubschema } from '../stitch/proxiedResult';
import { mergeFields } from '../stitch/mergeFields';

import { handleNull } from './results/handleNull';
import { handleObject } from './results/handleObject';
import { handleList } from './results/handleList';

export function checkResultAndHandleErrors(
result: ExecutionResult,
Expand Down Expand Up @@ -97,221 +80,3 @@ export function handleResult(
);
}
}

function handleList(
type: GraphQLList<any>,
list: Array<any>,
errors: ReadonlyArray<GraphQLError>,
subschema: GraphQLSchema | SubschemaConfig,
context: Record<string, any>,
info: IGraphQLToolsResolveInfo,
skipTypeMerging?: boolean,
) {
const childErrors = getErrorsByPathSegment(errors);

return list.map((listMember, index) =>
handleListMember(
getNullableType(type.ofType),
listMember,
index,
childErrors[index] != null ? childErrors[index] : [],
subschema,
context,
info,
skipTypeMerging,
),
);
}

function handleListMember(
type: GraphQLType,
listMember: any,
index: number,
errors: ReadonlyArray<GraphQLError>,
subschema: GraphQLSchema | SubschemaConfig,
context: Record<string, any>,
info: IGraphQLToolsResolveInfo,
skipTypeMerging?: boolean,
): any {
if (listMember == null) {
return handleNull(
info.fieldNodes,
[...responsePathAsArray(info.path), index],
errors,
);
}

if (isLeafType(type)) {
return type.parseValue(listMember);
} else if (isCompositeType(type)) {
return handleObject(
type,
listMember,
errors,
subschema,
context,
info,
skipTypeMerging,
);
} else if (isListType(type)) {
return handleList(
type,
listMember,
errors,
subschema,
context,
info,
skipTypeMerging,
);
}
}

export function handleObject(
type: GraphQLCompositeType,
object: any,
errors: ReadonlyArray<GraphQLError>,
subschema: GraphQLSchema | SubschemaConfig,
context: Record<string, any>,
info: IGraphQLToolsResolveInfo,
skipTypeMerging?: boolean,
) {
setErrors(
object,
errors.map((error) =>
relocatedError(
error,
error.nodes,
error.path != null ? error.path.slice(1) : undefined,
),
),
);

setObjectSubschema(object, subschema);

if (skipTypeMerging || !info.mergeInfo) {
return object;
}

const typeName = isAbstractType(type)
? info.schema.getTypeMap()[resolveFromParentTypename(object)].name
: type.name;
const mergedTypeInfo = info.mergeInfo.mergedTypes[typeName];
let targetSubschemas: Array<SubschemaConfig>;

if (mergedTypeInfo != null) {
targetSubschemas = mergedTypeInfo.subschemas;
}

if (!targetSubschemas) {
return object;
}

targetSubschemas = targetSubschemas.filter((s) => s !== subschema);
if (!targetSubschemas.length) {
return object;
}

const subFields = collectSubFields(info, object.__typename);

const selections = getFieldsNotInSubschema(
subFields,
subschema,
mergedTypeInfo,
object.__typename,
);

return mergeFields(
mergedTypeInfo,
typeName,
object,
selections,
[subschema as SubschemaConfig],
targetSubschemas,
context,
info,
);
}

function collectSubFields(info: IGraphQLToolsResolveInfo, typeName: string) {
let subFieldNodes: Record<string, Array<FieldNode>> = Object.create(null);
const visitedFragmentNames = Object.create(null);
info.fieldNodes.forEach((fieldNode) => {
subFieldNodes = collectFields(
({
schema: info.schema,
variableValues: info.variableValues,
fragments: info.fragments,
} as unknown) as ExecutionContext,
info.schema.getType(typeName) as GraphQLObjectType,
fieldNode.selectionSet,
subFieldNodes,
visitedFragmentNames,
);
});
return subFieldNodes;
}

function getFieldsNotInSubschema(
subFieldNodes: Record<string, Array<FieldNode>>,
subschema: GraphQLSchema | SubschemaConfig,
mergedTypeInfo: MergedTypeInfo,
typeName: string,
): Array<FieldNode> {
const typeMap = isSubschemaConfig(subschema)
? mergedTypeInfo.typeMaps.get(subschema)
: subschema.getTypeMap();
const fields = (typeMap[typeName] as GraphQLObjectType).getFields();

const fieldsNotInSchema: Array<FieldNode> = [];
Object.keys(subFieldNodes).forEach((responseName) => {
subFieldNodes[responseName].forEach((subFieldNode) => {
if (!fields[subFieldNode.name.value]) {
fieldsNotInSchema.push(subFieldNode);
}
});
});

return fieldsNotInSchema;
}

export function handleNull(
fieldNodes: ReadonlyArray<FieldNode>,
path: Array<string | number>,
errors: ReadonlyArray<GraphQLError>,
) {
if (errors.length) {
if (errors.some((error) => !error.path || error.path.length < 2)) {
return relocatedError(combineErrors(errors), fieldNodes, path);
} else if (errors.some((error) => typeof error.path[1] === 'string')) {
const childErrors = getErrorsByPathSegment(errors);

const result = Object.create(null);
Object.keys(childErrors).forEach((pathSegment) => {
result[pathSegment] = handleNull(
fieldNodes,
[...path, pathSegment],
childErrors[pathSegment],
);
});

return result;
}

const childErrors = getErrorsByPathSegment(errors);

const result: Array<any> = [];
Object.keys(childErrors).forEach((pathSegment) => {
result.push(
handleNull(
fieldNodes,
[...path, parseInt(pathSegment, 10)],
childErrors[pathSegment],
),
);
});

return result;
}

return null;
}
18 changes: 9 additions & 9 deletions src/delegate/delegateToSchema.ts
Expand Up @@ -20,18 +20,18 @@ import {
IGraphQLToolsResolveInfo,
Transform,
} from '../Interfaces';
import ExpandAbstractTypes from '../wrap/transforms/ExpandAbstractTypes';
import FilterToSchema from '../wrap/transforms/FilterToSchema';
import AddReplacementSelectionSets from '../wrap/transforms/AddReplacementSelectionSets';
import AddReplacementFragments from '../wrap/transforms/AddReplacementFragments';
import AddMergedTypeSelectionSets from '../wrap/transforms/AddMergedTypeSelectionSets';
import AddTypenameToAbstract from '../wrap/transforms/AddTypenameToAbstract';
import CheckResultAndHandleErrors from '../wrap/transforms/CheckResultAndHandleErrors';
import AddArgumentsAsVariables from '../wrap/transforms/AddArgumentsAsVariables';
import {
ExpandAbstractTypes,
FilterToSchema,
AddReplacementSelectionSets,
AddReplacementFragments,
AddMergedTypeSelectionSets,
AddTypenameToAbstract,
CheckResultAndHandleErrors,
applyRequestTransforms,
applyResultTransforms,
AddArgumentsAsVariables,
} from '../wrap/index';
} from '../wrap/transforms';

import linkToFetcher from '../stitch/linkToFetcher';
import { observableToAsyncIterable } from '../stitch/observableToAsyncIterable';
Expand Down
85 changes: 85 additions & 0 deletions src/delegate/results/handleList.ts
@@ -0,0 +1,85 @@
import {
GraphQLList,
GraphQLSchema,
GraphQLError,
getNullableType,
GraphQLType,
responsePathAsArray,
isLeafType,
isCompositeType,
isListType,
} from 'graphql';

import { SubschemaConfig, IGraphQLToolsResolveInfo } from '../../Interfaces';
import { getErrorsByPathSegment } from '../../stitch/errors';

import { handleNull } from './handleNull';
import { handleObject } from './handleObject';

export function handleList(
type: GraphQLList<any>,
list: Array<any>,
errors: ReadonlyArray<GraphQLError>,
subschema: GraphQLSchema | SubschemaConfig,
context: Record<string, any>,
info: IGraphQLToolsResolveInfo,
skipTypeMerging?: boolean,
) {
const childErrors = getErrorsByPathSegment(errors);

return list.map((listMember, index) =>
handleListMember(
getNullableType(type.ofType),
listMember,
index,
childErrors[index] != null ? childErrors[index] : [],
subschema,
context,
info,
skipTypeMerging,
),
);
}

function handleListMember(
type: GraphQLType,
listMember: any,
index: number,
errors: ReadonlyArray<GraphQLError>,
subschema: GraphQLSchema | SubschemaConfig,
context: Record<string, any>,
info: IGraphQLToolsResolveInfo,
skipTypeMerging?: boolean,
): any {
if (listMember == null) {
return handleNull(
info.fieldNodes,
[...responsePathAsArray(info.path), index],
errors,
);
}

if (isLeafType(type)) {
return type.parseValue(listMember);
} else if (isCompositeType(type)) {
return handleObject(
type,
listMember,
errors,
subschema,
context,
info,
skipTypeMerging,
);
} else if (isListType(type)) {
return handleList(
type,
listMember,
errors,
subschema,
context,
info,
skipTypeMerging,
);
}
}

0 comments on commit d8c43b4

Please sign in to comment.