Skip to content

Commit 4149722

Browse files
authored
deprecate (internal) buildResolveInfo in favor of (internal) ResolveInfo class (#4552)
Motivation: interface `GraphQLResolveInfo` upgraded in v17 to include a lazy `abortSignal` implemented by the new internal `ResolveInfo` class, available by deep import. Backporting `ResolveInfo` allows us to deprecate `buildResolveInfo` in v16 as in v17 this deep import is no longer available. Note: Change appreciable only to those using deep imports.
1 parent 4c057eb commit 4149722

File tree

4 files changed

+209
-0
lines changed

4 files changed

+209
-0
lines changed

src/execution/ResolveInfo.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import type { ObjMap } from '../jsutils/ObjMap';
2+
import type { Path } from '../jsutils/Path';
3+
4+
import type {
5+
FieldNode,
6+
FragmentDefinitionNode,
7+
OperationDefinitionNode,
8+
} from '../language/ast';
9+
10+
import type {
11+
GraphQLField,
12+
GraphQLObjectType,
13+
GraphQLOutputType,
14+
GraphQLResolveInfo,
15+
} from '../type/definition';
16+
import type { GraphQLSchema } from '../type/schema';
17+
18+
interface ExecutionDetails {
19+
schema: GraphQLSchema;
20+
fragments: ObjMap<FragmentDefinitionNode>;
21+
rootValue: unknown;
22+
operation: OperationDefinitionNode;
23+
variableValues: { [key: string]: unknown };
24+
}
25+
26+
/** @internal */
27+
/** @internal */
28+
export class ResolveInfo implements GraphQLResolveInfo {
29+
private _executionDetails: ExecutionDetails;
30+
private _fieldDef: GraphQLField<unknown, unknown>;
31+
private _fieldNodes: ReadonlyArray<FieldNode>;
32+
private _parentType: GraphQLObjectType;
33+
private _path: Path;
34+
35+
private _fieldName: string | undefined;
36+
private _returnType: GraphQLOutputType | undefined;
37+
private _schema: GraphQLSchema | undefined;
38+
private _fragments: ObjMap<FragmentDefinitionNode> | undefined;
39+
private _rootValue: unknown;
40+
private _rootValueDefined?: boolean;
41+
private _operation: OperationDefinitionNode | undefined;
42+
private _variableValues: { [key: string]: unknown } | undefined;
43+
44+
constructor(
45+
executionDetails: ExecutionDetails,
46+
fieldDef: GraphQLField<unknown, unknown>,
47+
fieldNodes: ReadonlyArray<FieldNode>,
48+
parentType: GraphQLObjectType,
49+
path: Path,
50+
) {
51+
this._executionDetails = executionDetails;
52+
this._fieldDef = fieldDef;
53+
this._fieldNodes = fieldNodes;
54+
this._parentType = parentType;
55+
this._path = path;
56+
}
57+
58+
get fieldName(): string {
59+
this._fieldName ??= this._fieldDef.name;
60+
return this._fieldName;
61+
}
62+
63+
get fieldNodes(): ReadonlyArray<FieldNode> {
64+
return this._fieldNodes;
65+
}
66+
67+
get returnType(): GraphQLOutputType {
68+
this._returnType ??= this._fieldDef.type;
69+
return this._returnType;
70+
}
71+
72+
get parentType(): GraphQLObjectType {
73+
return this._parentType;
74+
}
75+
76+
get path(): Path {
77+
return this._path;
78+
}
79+
80+
get schema(): GraphQLSchema {
81+
this._schema ??= this._executionDetails.schema;
82+
return this._schema;
83+
}
84+
85+
get fragments(): ObjMap<FragmentDefinitionNode> {
86+
this._fragments ??= this._executionDetails.fragments;
87+
return this._fragments;
88+
}
89+
90+
get rootValue(): unknown {
91+
if (!this._rootValueDefined) {
92+
this._rootValueDefined = true;
93+
this._rootValue = this._executionDetails.rootValue;
94+
}
95+
return this._rootValue;
96+
}
97+
98+
get operation(): OperationDefinitionNode {
99+
this._operation ??= this._executionDetails.operation;
100+
return this._operation;
101+
}
102+
103+
get variableValues(): { [key: string]: unknown } {
104+
this._variableValues ??= this._executionDetails.variableValues;
105+
return this._variableValues;
106+
}
107+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { expect } from 'chai';
2+
import { describe, it } from 'mocha';
3+
4+
import { invariant } from '../../jsutils/invariant';
5+
6+
import { Kind } from '../../language/kinds';
7+
import { parse } from '../../language/parser';
8+
9+
import { GraphQLObjectType } from '../../type/definition';
10+
import { GraphQLString } from '../../type/scalars';
11+
import { GraphQLSchema } from '../../type/schema';
12+
13+
import { collectFields } from '../collectFields';
14+
import { ResolveInfo } from '../ResolveInfo';
15+
16+
describe('ResolveInfo', () => {
17+
const query = new GraphQLObjectType({
18+
name: 'Query',
19+
fields: { test: { type: GraphQLString } },
20+
});
21+
22+
const document = parse('{ test }');
23+
24+
const operation = document.definitions[0];
25+
invariant(operation.kind === Kind.OPERATION_DEFINITION);
26+
27+
const executionDetails = {
28+
schema: new GraphQLSchema({ query }),
29+
operation,
30+
fragments: {},
31+
rootValue: { test: 'root' },
32+
variableValues: {},
33+
};
34+
35+
const { schema, fragments, rootValue, variableValues } = executionDetails;
36+
37+
const groupedFieldSet = collectFields(
38+
schema,
39+
fragments,
40+
variableValues,
41+
query,
42+
operation.selectionSet,
43+
);
44+
45+
const fieldDetailsList = groupedFieldSet.get('test');
46+
invariant(fieldDetailsList != null);
47+
48+
const path = { key: 'test', prev: undefined, typename: 'Query' };
49+
50+
const resolveInfo = new ResolveInfo(
51+
executionDetails,
52+
query.getFields().test,
53+
fieldDetailsList,
54+
query,
55+
path,
56+
);
57+
58+
it('exposes fieldName', () => {
59+
expect(resolveInfo.fieldName).to.equal('test');
60+
});
61+
62+
it('exposes fieldNodes', () => {
63+
const retrievedFieldNodes = resolveInfo.fieldNodes;
64+
expect(retrievedFieldNodes).to.deep.equal(fieldDetailsList);
65+
expect(retrievedFieldNodes).to.equal(resolveInfo.fieldNodes); // ensure same reference
66+
});
67+
68+
it('exposes returnType', () => {
69+
expect(resolveInfo.returnType).to.equal(query.getFields().test.type);
70+
});
71+
72+
it('exposes parentType', () => {
73+
expect(resolveInfo.parentType).to.equal(query);
74+
});
75+
76+
it('exposes path', () => {
77+
expect(resolveInfo.path).to.deep.equal(path);
78+
});
79+
80+
it('exposes schema', () => {
81+
expect(resolveInfo.schema).to.equal(schema);
82+
});
83+
84+
it('exposes fragments', () => {
85+
expect(resolveInfo.fragments).to.equal(fragments);
86+
});
87+
88+
it('exposes rootValue', () => {
89+
expect(resolveInfo.rootValue).to.equal(rootValue);
90+
});
91+
92+
it('exposes operation', () => {
93+
expect(resolveInfo.operation).to.equal(operation);
94+
});
95+
96+
it('exposes variableValues', () => {
97+
expect(resolveInfo.variableValues).to.equal(variableValues);
98+
});
99+
});

src/execution/execute.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ function executeField(
609609
}
610610

611611
/**
612+
* @deprecated will be removed in v17, use ResolveInfo class instead
612613
* @internal
613614
*/
614615
export function buildResolveInfo(

src/execution/subscribe.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
// eslint-disable-next-line import/no-deprecated
2323
assertValidExecutionArguments,
2424
buildExecutionContext,
25+
// eslint-disable-next-line import/no-deprecated
2526
buildResolveInfo,
2627
execute,
2728
getFieldDef,
@@ -228,6 +229,7 @@ async function executeSubscription(
228229
}
229230

230231
const path = addPath(undefined, responseName, rootType.name);
232+
// eslint-disable-next-line import/no-deprecated
231233
const info = buildResolveInfo(
232234
exeContext,
233235
fieldDef,

0 commit comments

Comments
 (0)