Skip to content

Commit

Permalink
Support projection build operation (#1556)
Browse files Browse the repository at this point in the history
* Support projectionn in buildOperationNode

* Add selectedFields

* Improvements

* Fix build

* Do not respect depthLimit if specific selection set provided
  • Loading branch information
ardatan committed May 29, 2020
1 parent 68c14f5 commit b7fba17
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 25 deletions.
1 change: 1 addition & 0 deletions packages/links/package.json
Expand Up @@ -23,6 +23,7 @@
"graphql-upload": "11.0.0"
},
"dependencies": {
"@graphql-tools/utils": "6.0.3",
"apollo-link": "1.2.14",
"apollo-upload-client": "13.0.0",
"form-data": "3.0.0",
Expand Down
65 changes: 40 additions & 25 deletions packages/utils/src/build-operation-for-field.ts
Expand Up @@ -51,6 +51,12 @@ export type Skip = string[];
export type Force = string[];
export type Ignore = string[];

export type SelectedFields =
| {
[key: string]: SelectedFields;
}
| boolean;

export function buildOperationNodeForField({
schema,
kind,
Expand All @@ -60,6 +66,7 @@ export function buildOperationNodeForField({
depthLimit,
circularReferenceDepth,
argNames,
selectedFields = true,
}: {
schema: GraphQLSchema;
kind: OperationTypeNode;
Expand All @@ -69,6 +76,7 @@ export function buildOperationNodeForField({
depthLimit?: number;
circularReferenceDepth?: number;
argNames?: string[];
selectedFields?: SelectedFields;
}) {
resetOperationVariables();
resetFieldMap();
Expand All @@ -82,6 +90,7 @@ export function buildOperationNodeForField({
depthLimit: depthLimit || Infinity,
circularReferenceDepth: circularReferenceDepth || 1,
argNames,
selectedFields,
});

// attach variables
Expand All @@ -102,6 +111,7 @@ function buildOperationAndCollectVariables({
depthLimit,
circularReferenceDepth,
argNames,
selectedFields,
}: {
schema: GraphQLSchema;
fieldName: string;
Expand All @@ -111,6 +121,7 @@ function buildOperationAndCollectVariables({
depthLimit: number;
circularReferenceDepth: number;
argNames?: string[];
selectedFields: SelectedFields;
}): OperationDefinitionNode {
const typeMap: Record<OperationTypeNode, GraphQLObjectType> = {
query: schema.getQueryType()!,
Expand Down Expand Up @@ -154,6 +165,7 @@ function buildOperationAndCollectVariables({
schema,
depth: 0,
argNames,
selectedFields,
}),
],
},
Expand All @@ -173,6 +185,7 @@ function resolveSelectionSet({
schema,
depth,
argNames,
selectedFields,
}: {
parent: GraphQLNamedType;
type: GraphQLNamedType;
Expand All @@ -185,9 +198,10 @@ function resolveSelectionSet({
circularReferenceDepth: number;
schema: GraphQLSchema;
depth: number;
selectedFields: SelectedFields;
argNames?: string[];
}): SelectionSetNode | void {
if (depth > depthLimit) {
if (typeof selectedFields === 'boolean' && depth > depthLimit) {
return;
}
if (isUnionType(type)) {
Expand Down Expand Up @@ -224,19 +238,11 @@ function resolveSelectionSet({
schema,
depth,
argNames,
selectedFields,
}) as SelectionSetNode,
};
})
.filter(f => {
if (f) {
if ('selectionSet' in f) {
return f.selectionSet?.selections?.length;
} else {
return true;
}
}
return false;
}),
.filter(fragmentNode => fragmentNode?.selectionSet?.selections?.length > 0),
};
}

Expand Down Expand Up @@ -276,9 +282,11 @@ function resolveSelectionSet({
schema,
depth,
argNames,
selectedFields,
}) as SelectionSetNode,
};
}),
})
.filter(fragmentNode => fragmentNode?.selectionSet?.selections?.length > 0),
};
}

Expand Down Expand Up @@ -312,19 +320,23 @@ function resolveSelectionSet({
});
})
.map(fieldName => {
return resolveField({
type: type,
field: fields[fieldName],
models,
path: [...path, fieldName],
ancestors,
ignore,
depthLimit,
circularReferenceDepth,
schema,
depth,
argNames,
});
const selectedSubFields = typeof selectedFields === 'object' ? selectedFields[fieldName] : true;
if (selectedSubFields) {
return resolveField({
type: type,
field: fields[fieldName],
models,
path: [...path, fieldName],
ancestors,
ignore,
depthLimit,
circularReferenceDepth,
schema,
depth,
argNames,
selectedFields: selectedSubFields,
});
}
})
.filter(f => {
if (f) {
Expand Down Expand Up @@ -398,6 +410,7 @@ function resolveField({
schema,
depth,
argNames,
selectedFields,
}: {
type: GraphQLObjectType;
field: GraphQLField<any, any>;
Expand All @@ -410,6 +423,7 @@ function resolveField({
circularReferenceDepth: number;
schema: GraphQLSchema;
depth: number;
selectedFields: SelectedFields;
argNames?: string[];
}): SelectionNode {
const namedType = getNamedType(field.type);
Expand Down Expand Up @@ -480,6 +494,7 @@ function resolveField({
schema,
depth: depth + 1,
argNames,
selectedFields,
}) || undefined,
arguments: args,
};
Expand Down
41 changes: 41 additions & 0 deletions packages/utils/tests/build-operation-node-for-field.spec.ts
Expand Up @@ -519,3 +519,44 @@ test('arguments', async () => {
`)
);
});

test('selectedFields', async () => {

const document = buildOperationNodeForField({
schema,
kind: 'query',
field: 'user',
selectedFields: {
favoriteFood: {
dough: true,
toppings: true,
asian: true,
},
shelf: true, // Add all nested fields
}
})!;

expect(clean(document)).toEqual(
clean(/* GraphQL */ `
query userQuery($id: ID!) {
user(id: $id) {
favoriteFood {
... on Pizza {
dough
toppings
}
... on Salad {
... on Coleslaw {
asian
}
}
}
shelf {
id
title
}
}
}
`)
);
})

0 comments on commit b7fba17

Please sign in to comment.