Skip to content

Commit 687e996

Browse files
captbaritonefacebook-github-bot
authored andcommitted
Add compiler validation to error on resolver that returns plural server type
Reviewed By: tyao1 Differential Revision: D70109268 fbshipit-source-id: a8a0fbc58ee2c3c4a22b6344799a7d4f1c348eef
1 parent cf4f547 commit 687e996

13 files changed

+175
-129
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
==================================== INPUT ====================================
2+
//- PersonComponent.js
3+
graphql`query PersonComponentQuery {
4+
plural_server @waterfall {
5+
name
6+
}
7+
}`
8+
9+
//- QueryResolvers.js
10+
/**
11+
* @RelayResolver Query.plural_server: [User]
12+
*/
13+
14+
//- relay.config.json
15+
{
16+
"language": "flow",
17+
"jsModuleFormat": "haste",
18+
"schema": "schema.graphql"
19+
}
20+
21+
//- schema.graphql
22+
type Query {
23+
greeting: String
24+
node(id: ID!): Node
25+
}
26+
27+
interface Node {
28+
id: ID!
29+
}
30+
31+
type User implements Node {
32+
id: ID!
33+
name: String
34+
}
35+
==================================== OUTPUT ===================================
36+
✖︎ Unexpected Relay Resolver returning plual edge to type defined on the server. Relay Resolvers do not curretly support returning plural edges to server types. As a work around, consider defining a plural edge to a client type which has a singular edge to the server type.
37+
38+
QueryResolvers.js:2:25
39+
1 │ *
40+
2 │ * @RelayResolver Query.plural_server: [User]
41+
│ ^^^^^^^^^^^^^
42+
3 │
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//- PersonComponent.js
2+
graphql`query PersonComponentQuery {
3+
plural_server @waterfall {
4+
name
5+
}
6+
}`
7+
8+
//- QueryResolvers.js
9+
/**
10+
* @RelayResolver Query.plural_server: [User]
11+
*/
12+
13+
//- relay.config.json
14+
{
15+
"language": "flow",
16+
"jsModuleFormat": "haste",
17+
"schema": "schema.graphql"
18+
}
19+
20+
//- schema.graphql
21+
type Query {
22+
greeting: String
23+
node(id: ID!): Node
24+
}
25+
26+
interface Node {
27+
id: ID!
28+
}
29+
30+
type User implements Node {
31+
id: ID!
32+
name: String
33+
}

compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<73cd290ab5054a643e2e27b6b67d7d1b>>
7+
* @generated SignedSource<<18ad721b702d974f6b034f393230738d>>
88
*/
99

1010
mod relay_compiler_integration;
@@ -278,6 +278,13 @@ async fn resolver_returns_interface_of_live_and_non_live_strong_model_type() {
278278
test_fixture(transform_fixture, file!(), "resolver_returns_interface_of_live_and_non_live_strong_model_type.input", "relay_compiler_integration/fixtures/resolver_returns_interface_of_live_and_non_live_strong_model_type.expected", input, expected).await;
279279
}
280280

281+
#[tokio::test]
282+
async fn resolver_returns_plural_server_type_invalid() {
283+
let input = include_str!("relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.input");
284+
let expected = include_str!("relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.expected");
285+
test_fixture(transform_fixture, file!(), "resolver_returns_plural_server_type.invalid.input", "relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.expected", input, expected).await;
286+
}
287+
281288
#[tokio::test]
282289
async fn resolver_returns_union_of_cse() {
283290
let input = include_str!("relay_compiler_integration/fixtures/resolver_returns_union_of_cse.input");

compiler/crates/relay-transforms/src/client_edges.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,12 @@ impl<'program, 'pc> ClientEdgesTransform<'program, 'pc> {
485485
waterfall_directive: Option<&Directive>,
486486
selections: Vec<Selection>,
487487
) -> ClientEdgeMetadataDirective {
488+
if field_type.type_.is_list() {
489+
self.errors.push(Diagnostic::error(
490+
ValidationMessage::ClientEdgeToServerObjectList,
491+
field_type.name.location,
492+
));
493+
}
488494
// Client Edges to server objects must be annotated with @waterfall
489495
if waterfall_directive.is_none() {
490496
self.errors.push(Diagnostic::error_with_data(

compiler/crates/relay-transforms/src/errors.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ pub enum ValidationMessage {
131131
name: StringKey,
132132
type_name: ObjectName,
133133
},
134+
135+
#[error(
136+
"Unexpected Relay Resolver returning plual edge to type defined on the server. Relay Resolvers do not curretly support returning plural edges to server types. As a work around, consider defining a plural edge to a client type which has a singular edge to the server type."
137+
)]
138+
ClientEdgeToServerObjectList,
139+
134140
#[error("Invalid directive combination. @alias may not be combined with other directives.")]
135141
FragmentAliasIncompatibleDirective,
136142

compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge-with-required-edge.expected

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,26 @@ fragment relayResolver_BestFriendResolverFragment_name on User {
55

66
query relayResolver_Query {
77
me {
8-
best_friends @waterfall {
8+
best_friends {
99
name
1010
}
1111
}
1212
}
1313

1414
# %extensions%
1515

16+
type ClientUser {
17+
name: String
18+
}
19+
1620
extend type User {
17-
best_friends: [User!] @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js")
21+
best_friends: [ClientUser!]
22+
@relay_resolver(
23+
fragment_name: "relayResolver_BestFriendResolverFragment_name"
24+
import_path: "./foo/bar/baz/BestFriendResolver.js"
25+
)
1826
}
1927
==================================== OUTPUT ===================================
20-
import type { RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType } from "RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends.graphql";
21-
export type ClientEdgeQuery_relayResolver_Query_me__best_friends$variables = {|
22-
id: string,
23-
|};
24-
export type ClientEdgeQuery_relayResolver_Query_me__best_friends$data = {|
25-
+node: ?{|
26-
+$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType,
27-
|},
28-
|};
29-
export type ClientEdgeQuery_relayResolver_Query_me__best_friends = {|
30-
response: ClientEdgeQuery_relayResolver_Query_me__best_friends$data,
31-
variables: ClientEdgeQuery_relayResolver_Query_me__best_friends$variables,
32-
|};
33-
-------------------------------------------------------------------------------
3428
import type { DataID } from "relay-runtime";
3529
import type { relayResolver_BestFriendResolverFragment_name$key } from "relayResolver_BestFriendResolverFragment_name.graphql";
3630
import userBestFriendsResolverType from "BestFriendResolver";
@@ -55,20 +49,6 @@ export type relayResolver_Query = {|
5549
|};
5650
-------------------------------------------------------------------------------
5751
import type { FragmentType } from "relay-runtime";
58-
declare export opaque type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType: FragmentType;
59-
import type { ClientEdgeQuery_relayResolver_Query_me__best_friends$variables } from "ClientEdgeQuery_relayResolver_Query_me__best_friends.graphql";
60-
export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data = {|
61-
+id: string,
62-
+name: ?string,
63-
+$fragmentType: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType,
64-
|};
65-
export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$key = {
66-
+$data?: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data,
67-
+$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType,
68-
...
69-
};
70-
-------------------------------------------------------------------------------
71-
import type { FragmentType } from "relay-runtime";
7252
declare export opaque type relayResolver_BestFriendResolverFragment_name$fragmentType: FragmentType;
7353
export type relayResolver_BestFriendResolverFragment_name$data = {|
7454
+name: ?string,

compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge-with-required-edge.graphql

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@ fragment relayResolver_BestFriendResolverFragment_name on User {
44

55
query relayResolver_Query {
66
me {
7-
best_friends @waterfall {
7+
best_friends {
88
name
99
}
1010
}
1111
}
1212

1313
# %extensions%
1414

15+
type ClientUser {
16+
name: String
17+
}
18+
1519
extend type User {
16-
best_friends: [User!] @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js")
20+
best_friends: [ClientUser!]
21+
@relay_resolver(
22+
fragment_name: "relayResolver_BestFriendResolverFragment_name"
23+
import_path: "./foo/bar/baz/BestFriendResolver.js"
24+
)
1725
}

compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge.expected

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,26 @@ fragment relayResolver_BestFriendResolverFragment_name on User {
55

66
query relayResolver_Query {
77
me {
8-
best_friends @waterfall {
8+
best_friends {
99
name
1010
}
1111
}
1212
}
1313

1414
# %extensions%
1515

16+
type ClientUser {
17+
name: String
18+
}
19+
1620
extend type User {
17-
best_friends: [User] @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js")
21+
best_friends: [ClientUser]
22+
@relay_resolver(
23+
fragment_name: "relayResolver_BestFriendResolverFragment_name"
24+
import_path: "./foo/bar/baz/BestFriendResolver.js"
25+
)
1826
}
1927
==================================== OUTPUT ===================================
20-
import type { RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType } from "RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends.graphql";
21-
export type ClientEdgeQuery_relayResolver_Query_me__best_friends$variables = {|
22-
id: string,
23-
|};
24-
export type ClientEdgeQuery_relayResolver_Query_me__best_friends$data = {|
25-
+node: ?{|
26-
+$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType,
27-
|},
28-
|};
29-
export type ClientEdgeQuery_relayResolver_Query_me__best_friends = {|
30-
response: ClientEdgeQuery_relayResolver_Query_me__best_friends$data,
31-
variables: ClientEdgeQuery_relayResolver_Query_me__best_friends$variables,
32-
|};
33-
-------------------------------------------------------------------------------
3428
import type { DataID } from "relay-runtime";
3529
import type { relayResolver_BestFriendResolverFragment_name$key } from "relayResolver_BestFriendResolverFragment_name.graphql";
3630
import userBestFriendsResolverType from "BestFriendResolver";
@@ -55,20 +49,6 @@ export type relayResolver_Query = {|
5549
|};
5650
-------------------------------------------------------------------------------
5751
import type { FragmentType } from "relay-runtime";
58-
declare export opaque type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType: FragmentType;
59-
import type { ClientEdgeQuery_relayResolver_Query_me__best_friends$variables } from "ClientEdgeQuery_relayResolver_Query_me__best_friends.graphql";
60-
export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data = {|
61-
+id: string,
62-
+name: ?string,
63-
+$fragmentType: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType,
64-
|};
65-
export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$key = {
66-
+$data?: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data,
67-
+$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType,
68-
...
69-
};
70-
-------------------------------------------------------------------------------
71-
import type { FragmentType } from "relay-runtime";
7252
declare export opaque type relayResolver_BestFriendResolverFragment_name$fragmentType: FragmentType;
7353
export type relayResolver_BestFriendResolverFragment_name$data = {|
7454
+name: ?string,

compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge.graphql

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@ fragment relayResolver_BestFriendResolverFragment_name on User {
44

55
query relayResolver_Query {
66
me {
7-
best_friends @waterfall {
7+
best_friends {
88
name
99
}
1010
}
1111
}
1212

1313
# %extensions%
1414

15+
type ClientUser {
16+
name: String
17+
}
18+
1519
extend type User {
16-
best_friends: [User] @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js")
20+
best_friends: [ClientUser]
21+
@relay_resolver(
22+
fragment_name: "relayResolver_BestFriendResolverFragment_name"
23+
import_path: "./foo/bar/baz/BestFriendResolver.js"
24+
)
1725
}

compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge-with-required-edge.expected

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,26 @@ fragment relayResolver_BestFriendResolverFragment_name on User {
55

66
query relayResolver_Query {
77
me {
8-
best_friends @waterfall {
8+
best_friends {
99
name
1010
}
1111
}
1212
}
1313

1414
# %extensions%
1515

16+
type ClientUser {
17+
name: String
18+
}
19+
1620
extend type User {
17-
best_friends: [User!]! @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js")
21+
best_friends: [ClientUser!]!
22+
@relay_resolver(
23+
fragment_name: "relayResolver_BestFriendResolverFragment_name"
24+
import_path: "./foo/bar/baz/BestFriendResolver.js"
25+
)
1826
}
1927
==================================== OUTPUT ===================================
20-
import type { RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType } from "RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends.graphql";
21-
export type ClientEdgeQuery_relayResolver_Query_me__best_friends$variables = {|
22-
id: string,
23-
|};
24-
export type ClientEdgeQuery_relayResolver_Query_me__best_friends$data = {|
25-
+node: ?{|
26-
+$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType,
27-
|},
28-
|};
29-
export type ClientEdgeQuery_relayResolver_Query_me__best_friends = {|
30-
response: ClientEdgeQuery_relayResolver_Query_me__best_friends$data,
31-
variables: ClientEdgeQuery_relayResolver_Query_me__best_friends$variables,
32-
|};
33-
-------------------------------------------------------------------------------
3428
import type { DataID } from "relay-runtime";
3529
import type { relayResolver_BestFriendResolverFragment_name$key } from "relayResolver_BestFriendResolverFragment_name.graphql";
3630
import userBestFriendsResolverType from "BestFriendResolver";
@@ -55,20 +49,6 @@ export type relayResolver_Query = {|
5549
|};
5650
-------------------------------------------------------------------------------
5751
import type { FragmentType } from "relay-runtime";
58-
declare export opaque type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType: FragmentType;
59-
import type { ClientEdgeQuery_relayResolver_Query_me__best_friends$variables } from "ClientEdgeQuery_relayResolver_Query_me__best_friends.graphql";
60-
export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data = {|
61-
+id: string,
62-
+name: ?string,
63-
+$fragmentType: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType,
64-
|};
65-
export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$key = {
66-
+$data?: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data,
67-
+$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType,
68-
...
69-
};
70-
-------------------------------------------------------------------------------
71-
import type { FragmentType } from "relay-runtime";
7252
declare export opaque type relayResolver_BestFriendResolverFragment_name$fragmentType: FragmentType;
7353
export type relayResolver_BestFriendResolverFragment_name$data = {|
7454
+name: ?string,

0 commit comments

Comments
 (0)