Skip to content

Commit 55dff35

Browse files
captbaritonefacebook-github-bot
authored andcommitted
Support @catch on @connection
Reviewed By: itamark Differential Revision: D69663902 fbshipit-source-id: d340f745188f44a174738c6167e6999953ae3539
1 parent 16f962c commit 55dff35

8 files changed

+288
-10
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use graphql_ir::Transformer;
2626
use graphql_ir::associated_data_impl;
2727
use intern::intern;
2828
use lazy_static::lazy_static;
29-
mod catchable_node;
29+
pub(crate) mod catchable_node;
3030
mod validation_message;
3131

3232
use self::catchable_node::CatchMetadata;

compiler/crates/relay-transforms/src/connections/connection_util.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8+
use std::str::FromStr;
9+
use std::vec;
10+
811
use common::Location;
912
use common::NamedItem;
1013
use common::WithLocation;
@@ -19,6 +22,8 @@ use schema::SDLSchema;
1922
use schema::Schema;
2023
use schema::Type;
2124

25+
use crate::CatchTo;
26+
use crate::catch_directive::catchable_node::CatchableNode;
2227
use crate::connections::ConnectionConstants;
2328
use crate::connections::ConnectionInterface;
2429
use crate::util::extract_variable_name;
@@ -91,6 +96,22 @@ pub fn build_connection_metadata(
9196
.arguments
9297
.named(connection_constants.last_arg_name);
9398

99+
let catch_metadata = connection_field.catch_metadata().unwrap();
100+
101+
// This checks if there's a catch and that catch is not null
102+
let is_catch_to_result = match catch_metadata {
103+
// catch does exist
104+
Some(catch_metadata) => {
105+
match catch_metadata.to {
106+
// catch is not null
107+
Some(arg) => arg != CatchTo::Null,
108+
// catch has no argument - default is Result
109+
None => true,
110+
}
111+
}
112+
None => false,
113+
};
114+
94115
let direction = match (first_arg, last_arg) {
95116
(Some(_), Some(_)) => connection_constants.direction_bidirectional,
96117
(Some(_), None) => connection_constants.direction_forward,
@@ -100,6 +121,19 @@ pub fn build_connection_metadata(
100121
),
101122
};
102123

124+
// We look for the path in connections to determine hasNext.
125+
// This path needs to include the value field to be correct for Result<> types
126+
let connection_path = match path {
127+
Some(path) => {
128+
let mut interim_path = path.clone();
129+
if is_catch_to_result {
130+
interim_path.push(StringKey::from_str("value").unwrap());
131+
}
132+
Some(interim_path)
133+
}
134+
None => path.clone(),
135+
};
136+
103137
ConnectionMetadata {
104138
first: extract_variable_name(first_arg),
105139
last: extract_variable_name(last_arg),
@@ -118,7 +152,7 @@ pub fn build_connection_metadata(
118152
)
119153
}),
120154
direction,
121-
path: path.clone(),
155+
path: connection_path,
122156
is_stream_connection,
123157
is_prefetchable_pagination,
124158
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
==================================== INPUT ====================================
2+
fragment PaginationFragment on Node
3+
@refetchable(queryName: "RefetchableFragmentQuery")
4+
@argumentDefinitions(
5+
count: {type: "Int", defaultValue: 10}
6+
cursor: {type: "ID"}
7+
) {
8+
id
9+
... on User {
10+
name
11+
friends(after: $cursor, first: $count)
12+
@connection(key: "PaginationFragment_friends") @catch(to: NULL) {
13+
edges {
14+
node {
15+
id
16+
}
17+
}
18+
}
19+
}
20+
}
21+
==================================== OUTPUT ===================================
22+
query RefetchableFragmentQuery(
23+
$count: Int = 10
24+
$cursor: ID
25+
$id: ID!
26+
) @__RefetchableDerivedFromMetadata
27+
# RefetchableDerivedFromMetadata(
28+
# FragmentDefinitionName(
29+
# "PaginationFragment",
30+
# ),
31+
# )
32+
{
33+
node(id: $id) {
34+
...PaginationFragment @arguments(count: $count, cursor: $cursor)
35+
}
36+
}
37+
38+
fragment PaginationFragment on Node @refetchable(queryName: "RefetchableFragmentQuery") @argumentDefinitions(
39+
count: {type: "Int", defaultValue: 10}
40+
cursor: {type: "ID"}
41+
) @__ConnectionMetadataDirective
42+
# ConnectionMetadataDirective(
43+
# [
44+
# ConnectionMetadata {
45+
# path: Some(
46+
# [
47+
# "friends",
48+
# ],
49+
# ),
50+
# direction: "forward",
51+
# first: Some(
52+
# "count",
53+
# ),
54+
# last: None,
55+
# before: None,
56+
# after: Some(
57+
# "cursor",
58+
# ),
59+
# is_stream_connection: false,
60+
# is_prefetchable_pagination: false,
61+
# },
62+
# ],
63+
# )
64+
@__RefetchableMetadata
65+
# RefetchableMetadata {
66+
# operation_name: OperationDefinitionName(
67+
# "RefetchableFragmentQuery",
68+
# ),
69+
# path: [
70+
# "node",
71+
# ],
72+
# identifier_info: Some(
73+
# RefetchableIdentifierInfo {
74+
# identifier_field: "id",
75+
# identifier_query_variable_name: "id",
76+
# },
77+
# ),
78+
# is_prefetchable_pagination: false,
79+
# }
80+
{
81+
id
82+
... on User {
83+
name
84+
friends(after: $cursor, first: $count) @catch(to: NULL) @__clientField(key: "PaginationFragment_friends", handle: "connection", filters: null, dynamicKey_UNSTABLE: null) {
85+
edges {
86+
node {
87+
id
88+
}
89+
... on FriendsEdge {
90+
cursor
91+
node {
92+
__typename
93+
}
94+
}
95+
}
96+
pageInfo {
97+
... on PageInfo {
98+
endCursor
99+
hasNextPage
100+
}
101+
}
102+
}
103+
}
104+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
fragment PaginationFragment on Node
2+
@refetchable(queryName: "RefetchableFragmentQuery")
3+
@argumentDefinitions(
4+
count: {type: "Int", defaultValue: 10}
5+
cursor: {type: "ID"}
6+
) {
7+
id
8+
... on User {
9+
name
10+
friends(after: $cursor, first: $count)
11+
@connection(key: "PaginationFragment_friends") @catch(to: NULL) {
12+
edges {
13+
node {
14+
id
15+
}
16+
}
17+
}
18+
}
19+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
==================================== INPUT ====================================
2+
fragment PaginationFragment on Node
3+
@refetchable(queryName: "RefetchableFragmentQuery")
4+
@argumentDefinitions(
5+
count: {type: "Int", defaultValue: 10}
6+
cursor: {type: "ID"}
7+
) {
8+
id
9+
... on User {
10+
name
11+
friends(after: $cursor, first: $count)
12+
@connection(key: "PaginationFragment_friends") @catch {
13+
edges {
14+
node {
15+
id
16+
}
17+
}
18+
}
19+
}
20+
}
21+
==================================== OUTPUT ===================================
22+
query RefetchableFragmentQuery(
23+
$count: Int = 10
24+
$cursor: ID
25+
$id: ID!
26+
) @__RefetchableDerivedFromMetadata
27+
# RefetchableDerivedFromMetadata(
28+
# FragmentDefinitionName(
29+
# "PaginationFragment",
30+
# ),
31+
# )
32+
{
33+
node(id: $id) {
34+
...PaginationFragment @arguments(count: $count, cursor: $cursor)
35+
}
36+
}
37+
38+
fragment PaginationFragment on Node @refetchable(queryName: "RefetchableFragmentQuery") @argumentDefinitions(
39+
count: {type: "Int", defaultValue: 10}
40+
cursor: {type: "ID"}
41+
) @__ConnectionMetadataDirective
42+
# ConnectionMetadataDirective(
43+
# [
44+
# ConnectionMetadata {
45+
# path: Some(
46+
# [
47+
# "friends",
48+
# "value",
49+
# ],
50+
# ),
51+
# direction: "forward",
52+
# first: Some(
53+
# "count",
54+
# ),
55+
# last: None,
56+
# before: None,
57+
# after: Some(
58+
# "cursor",
59+
# ),
60+
# is_stream_connection: false,
61+
# is_prefetchable_pagination: false,
62+
# },
63+
# ],
64+
# )
65+
@__RefetchableMetadata
66+
# RefetchableMetadata {
67+
# operation_name: OperationDefinitionName(
68+
# "RefetchableFragmentQuery",
69+
# ),
70+
# path: [
71+
# "node",
72+
# ],
73+
# identifier_info: Some(
74+
# RefetchableIdentifierInfo {
75+
# identifier_field: "id",
76+
# identifier_query_variable_name: "id",
77+
# },
78+
# ),
79+
# is_prefetchable_pagination: false,
80+
# }
81+
{
82+
id
83+
... on User {
84+
name
85+
friends(after: $cursor, first: $count) @catch @__clientField(key: "PaginationFragment_friends", handle: "connection", filters: null, dynamicKey_UNSTABLE: null) {
86+
edges {
87+
node {
88+
id
89+
}
90+
... on FriendsEdge {
91+
cursor
92+
node {
93+
__typename
94+
}
95+
}
96+
}
97+
pageInfo {
98+
... on PageInfo {
99+
endCursor
100+
hasNextPage
101+
}
102+
}
103+
}
104+
}
105+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
fragment PaginationFragment on Node
2+
@refetchable(queryName: "RefetchableFragmentQuery")
3+
@argumentDefinitions(
4+
count: {type: "Int", defaultValue: 10}
5+
cursor: {type: "ID"}
6+
) {
7+
id
8+
... on User {
9+
name
10+
friends(after: $cursor, first: $count)
11+
@connection(key: "PaginationFragment_friends") @catch {
12+
edges {
13+
node {
14+
id
15+
}
16+
}
17+
}
18+
}
19+
}

packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentCatchTestFragment.graphql.js

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

packages/react-relay/relay-hooks/__tests__/usePaginationFragmentCatch-test.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
import TestRenderer from 'react-test-renderer';
2121
import {createMockEnvironment} from 'relay-test-utils';
2222

23-
test('Repro for bug where @catch breaks the hasNext value returned by usePaginationFragment', async () => {
23+
test('@catch does not interfere with the hasNext value returned by usePaginationFragment', async () => {
2424
const QUERY = graphql`
2525
query usePaginationFragmentCatchTestQuery($first: Int, $after: ID) {
2626
me {
@@ -103,9 +103,5 @@ test('Repro for bug where @catch breaks the hasNext value returned by usePaginat
103103
});
104104
});
105105

106-
// This is the expected behavior and what we see without @catch
107-
// expect(container?.toJSON()).toBe('Connection has more items');
108-
109-
// This is the INCORRECT behavior introducted by a bug with @catch
110-
expect(container?.toJSON()).toBe('Connection has NO more items');
106+
expect(container?.toJSON()).toBe('Connection has more items');
111107
});

0 commit comments

Comments
 (0)