Skip to content

Commit 6f28c5d

Browse files
authored
feat: initial support for field resolvers in connect (#2290)
1 parent a1fb0b8 commit 6f28c5d

File tree

44 files changed

+12302
-3695
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+12302
-3695
lines changed

composition-go/index.global.js

Lines changed: 169 additions & 169 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

composition/src/utils/string-constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export const CONFIGURE_DESCRIPTION = 'openfed__configureDescription';
1717
export const CONFIGURE_CHILD_DESCRIPTIONS = 'openfed__configureChildDescriptions';
1818
export const CONSUMER_INACTIVE_THRESHOLD = 'consumerInactiveThreshold';
1919
export const CONSUMER_NAME = 'consumerName';
20+
export const CONNECT_FIELD_RESOLVER = 'connect__fieldResolver';
21+
export const CONTEXT = 'context';
2022
export const DEFAULT = 'default';
2123
export const DEFAULT_EDFS_PROVIDER_ID = 'default';
2224
export const DEFAULT_MUTATION = 'Mutation';

composition/src/v1/constants/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
COMPOSE_DIRECTIVE,
66
CONFIGURE_CHILD_DESCRIPTIONS,
77
CONFIGURE_DESCRIPTION,
8+
CONNECT_FIELD_RESOLVER,
89
DEPRECATED,
910
EDFS_KAFKA_PUBLISH,
1011
EDFS_KAFKA_SUBSCRIBE,
@@ -43,6 +44,7 @@ import {
4344
COMPOSE_DIRECTIVE_DEFINITION,
4445
CONFIGURE_CHILD_DESCRIPTIONS_DEFINITION,
4546
CONFIGURE_DESCRIPTION_DEFINITION,
47+
CONNECT_FIELD_RESOLVER_DEFINITION,
4648
DEPRECATED_DEFINITION,
4749
EDFS_KAFKA_PUBLISH_DEFINITION,
4850
EDFS_KAFKA_SUBSCRIBE_DEFINITION,
@@ -78,6 +80,7 @@ export const DIRECTIVE_DEFINITION_BY_NAME: ReadonlyMap<DirectiveName, DirectiveD
7880
[COMPOSE_DIRECTIVE, COMPOSE_DIRECTIVE_DEFINITION],
7981
[CONFIGURE_DESCRIPTION, CONFIGURE_DESCRIPTION_DEFINITION],
8082
[CONFIGURE_CHILD_DESCRIPTIONS, CONFIGURE_CHILD_DESCRIPTIONS_DEFINITION],
83+
[CONNECT_FIELD_RESOLVER, CONNECT_FIELD_RESOLVER_DEFINITION],
8184
[DEPRECATED, DEPRECATED_DEFINITION],
8285
[EDFS_KAFKA_PUBLISH, EDFS_KAFKA_PUBLISH_DEFINITION],
8386
[EDFS_KAFKA_SUBSCRIBE, EDFS_KAFKA_SUBSCRIBE_DEFINITION],

composition/src/v1/constants/directive-definitions.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
CONDITION,
1515
CONFIGURE_CHILD_DESCRIPTIONS,
1616
CONFIGURE_DESCRIPTION,
17+
CONNECT_FIELD_RESOLVER,
18+
CONTEXT,
1719
DEFAULT_EDFS_PROVIDER_ID,
1820
DEPRECATED,
1921
DESCRIPTION_OVERRIDE,
@@ -30,7 +32,6 @@ import {
3032
EXTENDS,
3133
EXTERNAL,
3234
FIELD_DEFINITION_UPPER,
33-
FIELD_SET_SCALAR,
3435
FIELDS,
3536
FOR,
3637
FROM,
@@ -184,6 +185,21 @@ export const CONFIGURE_DESCRIPTION_DEFINITION: DirectiveDefinitionNode = {
184185
repeatable: false,
185186
};
186187

188+
// directive @connect__fieldResolver(context: openfed__FieldSet!) on FIELD_DEFINITION
189+
export const CONNECT_FIELD_RESOLVER_DEFINITION: DirectiveDefinitionNode = {
190+
arguments: [
191+
{
192+
kind: Kind.INPUT_VALUE_DEFINITION,
193+
name: stringToNameNode(CONTEXT),
194+
type: REQUIRED_FIELDSET_TYPE_NODE,
195+
},
196+
],
197+
kind: Kind.DIRECTIVE_DEFINITION,
198+
locations: stringArrayToNameNodeArray([FIELD_DEFINITION_UPPER]),
199+
name: stringToNameNode(CONNECT_FIELD_RESOLVER),
200+
repeatable: false,
201+
};
202+
187203
export const DEPRECATED_DEFINITION: DirectiveDefinitionNode = {
188204
arguments: [
189205
{

composition/src/v1/constants/strings.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
AND_UPPER,
33
ARGUMENT_DEFINITION_UPPER,
44
BOOLEAN_SCALAR,
5+
CONNECT_FIELD_RESOLVER,
56
CONSUMER_INACTIVE_THRESHOLD,
67
CONSUMER_NAME,
78
DEPRECATED,
@@ -108,6 +109,7 @@ export const DEPENDENCIES_BY_DIRECTIVE_NAME: ReadonlyMap<DirectiveName, Array<De
108109
DirectiveName,
109110
Array<DefinitionNode>
110111
>([
112+
[CONNECT_FIELD_RESOLVER, [FIELD_SET_SCALAR_DEFINITION]],
111113
[EDFS_NATS_SUBSCRIBE, [EDFS_NATS_STREAM_CONFIGURATION_DEFINITION]],
112114
[KEY, [FIELD_SET_SCALAR_DEFINITION]],
113115
[LINK, [LINK_IMPORT_DEFINITION, LINK_PURPOSE_DEFINITION]],

composition/src/v1/normalization/directive-definition-data.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
CONDITION,
1313
CONFIGURE_CHILD_DESCRIPTIONS,
1414
CONFIGURE_DESCRIPTION,
15+
CONNECT_FIELD_RESOLVER,
16+
CONTEXT,
1517
DEFAULT_EDFS_PROVIDER_ID,
1618
DEPRECATED,
1719
DESCRIPTION_OVERRIDE,
@@ -79,6 +81,7 @@ import {
7981
COMPOSE_DIRECTIVE_DEFINITION,
8082
CONFIGURE_CHILD_DESCRIPTIONS_DEFINITION,
8183
CONFIGURE_DESCRIPTION_DEFINITION,
84+
CONNECT_FIELD_RESOLVER_DEFINITION,
8285
DEPRECATED_DEFINITION,
8386
EDFS_KAFKA_PUBLISH_DEFINITION,
8487
EDFS_KAFKA_SUBSCRIBE_DEFINITION,
@@ -206,6 +209,24 @@ export const CONFIGURE_CHILD_DESCRIPTIONS_DEFINITION_DATA: DirectiveDefinitionDa
206209
requiredArgumentNames: new Set<string>(),
207210
};
208211

212+
export const CONNECT_FIELD_RESOLVER_DEFINITION_DATA: DirectiveDefinitionData = {
213+
argumentTypeNodeByName: new Map<string, ArgumentData>([
214+
[
215+
CONTEXT,
216+
{
217+
name: CONTEXT,
218+
typeNode: REQUIRED_FIELDSET_TYPE_NODE,
219+
},
220+
],
221+
]),
222+
isRepeatable: false,
223+
locations: new Set<string>([FIELD_DEFINITION_UPPER]),
224+
name: CONNECT_FIELD_RESOLVER,
225+
node: CONNECT_FIELD_RESOLVER_DEFINITION,
226+
optionalArgumentNames: new Set<string>(),
227+
requiredArgumentNames: new Set<string>([CONTEXT]),
228+
};
229+
209230
export const DEPRECATED_DEFINITION_DATA: DirectiveDefinitionData = {
210231
argumentTypeNodeByName: new Map<string, ArgumentData>([
211232
[

composition/src/v1/normalization/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
COMPOSE_DIRECTIVE_DEFINITION_DATA,
2626
CONFIGURE_CHILD_DESCRIPTIONS_DEFINITION_DATA,
2727
CONFIGURE_DESCRIPTION_DEFINITION_DATA,
28+
CONNECT_FIELD_RESOLVER_DEFINITION_DATA,
2829
DEPRECATED_DEFINITION_DATA,
2930
EXTENDS_DEFINITION_DATA,
3031
EXTERNAL_DEFINITION_DATA,
@@ -56,6 +57,7 @@ import {
5657
COMPOSE_DIRECTIVE,
5758
CONFIGURE_CHILD_DESCRIPTIONS,
5859
CONFIGURE_DESCRIPTION,
60+
CONNECT_FIELD_RESOLVER,
5961
DEPRECATED,
6062
EDFS_KAFKA_PUBLISH,
6163
EDFS_KAFKA_SUBSCRIBE,
@@ -394,6 +396,7 @@ export function initializeDirectiveDefinitionDatas(): Map<string, DirectiveDefin
394396
[COMPOSE_DIRECTIVE, COMPOSE_DIRECTIVE_DEFINITION_DATA],
395397
[CONFIGURE_DESCRIPTION, CONFIGURE_DESCRIPTION_DEFINITION_DATA],
396398
[CONFIGURE_CHILD_DESCRIPTIONS, CONFIGURE_CHILD_DESCRIPTIONS_DEFINITION_DATA],
399+
[CONNECT_FIELD_RESOLVER, CONNECT_FIELD_RESOLVER_DEFINITION_DATA],
397400
[DEPRECATED, DEPRECATED_DEFINITION_DATA],
398401
[EDFS_KAFKA_PUBLISH, KAFKA_PUBLISH_DEFINITION_DATA],
399402
[EDFS_KAFKA_SUBSCRIBE, KAFKA_SUBSCRIBE_DEFINITION_DATA],
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { describe, expect, test } from 'vitest';
2+
import {
3+
CONNECT_FIELD_RESOLVER,
4+
CONTEXT,
5+
invalidDirectiveError,
6+
NormalizationFailure,
7+
NormalizationSuccess,
8+
normalizeSubgraph,
9+
ROUTER_COMPATIBILITY_VERSION_ONE,
10+
Subgraph,
11+
undefinedRequiredArgumentsErrorMessage,
12+
} from '../../../src';
13+
import { parse, printSchema } from 'graphql';
14+
import {
15+
normalizeString,
16+
normalizeSubgraphFailure,
17+
normalizeSubgraphSuccess,
18+
schemaToSortedNormalizedString,
19+
} from '../../utils/utils';
20+
import { CONNECT_FIELD_RESOLVER_DIRECTIVE, OPENFED_FIELD_SET, SCHEMA_QUERY_DEFINITION } from '../utils/utils';
21+
22+
describe('@connect__fieldResolver tests', () => {
23+
test('that @connect__fieldResolver is automatically included in the subgraph schema if it is referenced', () => {
24+
const { schema, warnings } = normalizeSubgraphSuccess(
25+
subgraphWithConnectConfigureResolver,
26+
ROUTER_COMPATIBILITY_VERSION_ONE,
27+
);
28+
expect(warnings).toHaveLength(0);
29+
expect(schemaToSortedNormalizedString(schema)).toBe(
30+
normalizeString(
31+
SCHEMA_QUERY_DEFINITION +
32+
CONNECT_FIELD_RESOLVER_DIRECTIVE +
33+
`
34+
type Foo {
35+
bar(baz: String!): String @connect__fieldResolver(context: "id")
36+
id: ID!
37+
}
38+
39+
type Query {
40+
foo: Foo!
41+
}
42+
` +
43+
OPENFED_FIELD_SET,
44+
),
45+
);
46+
});
47+
48+
test('that @connect__fieldResolver needs to have a context', () => {
49+
const { errors } = normalizeSubgraphFailure(
50+
subgraphWithConnectConfigureResolverWithoutContext,
51+
ROUTER_COMPATIBILITY_VERSION_ONE,
52+
);
53+
54+
expect(errors).toHaveLength(1);
55+
expect(errors[0]).toStrictEqual(
56+
invalidDirectiveError(CONNECT_FIELD_RESOLVER, `Foo.bar`, `1st`, [
57+
undefinedRequiredArgumentsErrorMessage(CONNECT_FIELD_RESOLVER, [CONTEXT], []),
58+
]),
59+
);
60+
});
61+
});
62+
63+
const subgraphWithConnectConfigureResolver: Subgraph = {
64+
name: 'connect-configure-resolver',
65+
url: '',
66+
definitions: parse(`
67+
type Foo {
68+
bar(baz: String!): String @connect__fieldResolver(context: "id")
69+
id: ID!
70+
}
71+
72+
type Query {
73+
foo: Foo!
74+
}
75+
`),
76+
};
77+
78+
const subgraphWithConnectConfigureResolverWithoutContext: Subgraph = {
79+
name: 'connect-configure-resolver-without-context',
80+
url: '',
81+
definitions: parse(`
82+
type Foo {
83+
bar(baz: String!): String @connect__fieldResolver
84+
id: ID!
85+
}
86+
`),
87+
};

composition/tests/v1/utils/utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ export const CONFIGURE_DESCRIPTION_DIRECTIVE = `
99
directive @openfed__configureDescription(descriptionOverride: String, propagate: Boolean! = true) on ARGUMENT_DEFINITION | ENUM | ENUM_VALUE | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | SCHEMA | UNION
1010
`;
1111

12+
export const CONNECT_FIELD_RESOLVER_DIRECTIVE = `
13+
directive @connect__fieldResolver(context: openfed__FieldSet!) on FIELD_DEFINITION
14+
`;
15+
1216
export const EDFS_NATS_PUBLISH_DIRECTIVE = `
1317
directive @edfs__natsPublish(providerId: String! = "default", subject: String!) on FIELD_DEFINITION
1418
`;

0 commit comments

Comments
 (0)