Skip to content

Commit f249f97

Browse files
authored
Implement catch result type (#549)
* implement catch result type * changelog * point to correct compiler * stable tests
1 parent a8a95de commit f249f97

12 files changed

+1522
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# master
22

3+
- Add support for the new Relay `@catch` directive. https://github.com/zth/rescript-relay/pull/549
4+
35
# 3.1.0
46

57
This brings the Relay version to `18.2.0`.
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
require("@testing-library/jest-dom/extend-expect");
2+
const t = require("@testing-library/react");
3+
const React = require("react");
4+
const queryMock = require("./queryMock");
5+
6+
const { test_catch } = require("./Test_catch.bs");
7+
8+
const date = new Date("2025-01-01T06:00");
9+
10+
describe("Catch", () => {
11+
test("logged in user prop - success", async () => {
12+
queryMock.mockQuery({
13+
name: "TestCatchLoggedInUserPropQuery",
14+
data: {
15+
loggedInUser: {
16+
id: "user-1",
17+
createdAt: date.toISOString(),
18+
},
19+
},
20+
});
21+
22+
t.render(test_catch("TestLoggedInUserProp"));
23+
await t.screen.findByText("Got createdAt: 2025-01-01");
24+
});
25+
26+
test("logged in user prop - fail", async () => {
27+
queryMock.mockQuery({
28+
name: "TestCatchLoggedInUserPropQuery",
29+
data: {
30+
loggedInUser: {
31+
id: "user-1",
32+
createdAt: null,
33+
},
34+
},
35+
graphqlErrors: [{ path: ["loggedInUser", "createdAt"] }],
36+
});
37+
38+
t.render(test_catch("TestLoggedInUserProp"));
39+
await t.screen.findByText("Error!");
40+
});
41+
42+
test("logged in user prop from fragment - success", async () => {
43+
queryMock.mockQuery({
44+
name: "TestCatchLoggedInUserPropQuery",
45+
data: {
46+
loggedInUser: {
47+
id: "user-1",
48+
createdAt: date.toISOString(),
49+
},
50+
},
51+
});
52+
53+
t.render(test_catch("TestLoggedInUserPropFragmentData"));
54+
await t.screen.findByText("Got createdAt: 2025-01-01");
55+
});
56+
57+
test("logged in user prop from fragment - fail", async () => {
58+
queryMock.mockQuery({
59+
name: "TestCatchLoggedInUserPropQuery",
60+
data: {
61+
loggedInUser: {
62+
id: "user-1",
63+
createdAt: null,
64+
},
65+
},
66+
graphqlErrors: [{ path: ["loggedInUser", "createdAt"] }],
67+
});
68+
69+
t.render(test_catch("TestLoggedInUserPropFragmentData"));
70+
await t.screen.findByText("Error!");
71+
});
72+
73+
test("member prop - success", async () => {
74+
queryMock.mockQuery({
75+
name: "TestCatchMemberPropQuery",
76+
data: {
77+
member: {
78+
__typename: "User",
79+
id: "user-1",
80+
createdAt: date.toISOString(),
81+
},
82+
},
83+
});
84+
85+
t.render(test_catch("TestMember"));
86+
await t.screen.findByText("Got user id: user-1, and createdAt: 2025-01-01");
87+
});
88+
89+
test("member prop - fail", async () => {
90+
queryMock.mockQuery({
91+
name: "TestCatchMemberPropQuery",
92+
data: {
93+
member: null,
94+
},
95+
graphqlErrors: [{ path: ["member"] }],
96+
});
97+
98+
t.render(test_catch("TestMember"));
99+
await t.screen.findByText("Error!");
100+
});
101+
102+
test("member prop - success nested", async () => {
103+
queryMock.mockQuery({
104+
name: "TestCatchMemberPropNestedQuery",
105+
data: {
106+
member: {
107+
__typename: "User",
108+
id: "user-1",
109+
memberOfSingular: {
110+
__typename: "User",
111+
id: "user-2",
112+
createdAt: date.toISOString(),
113+
},
114+
},
115+
},
116+
});
117+
118+
t.render(test_catch("TestMemberNested"));
119+
await t.screen.findByText("Got user id: user-1, and createdAt: 2025-01-01");
120+
});
121+
122+
test("member prop - fail nested", async () => {
123+
queryMock.mockQuery({
124+
name: "TestCatchMemberPropNestedQuery",
125+
data: {
126+
member: {
127+
__typename: "User",
128+
id: "user-1",
129+
memberOfSingular: {
130+
__typename: "User",
131+
id: "user-2",
132+
createdAt: null,
133+
},
134+
},
135+
},
136+
graphqlErrors: [{ path: ["member", "memberOfSingular", "createdAt"] }],
137+
});
138+
139+
t.render(test_catch("TestMemberNested"));
140+
await t.screen.findByText("Error nested!");
141+
});
142+
143+
test("members array", async () => {
144+
queryMock.mockQuery({
145+
name: "TestCatchMembersPropQuery",
146+
data: {
147+
members: {
148+
edges: [
149+
{
150+
__typename: "UserEdge",
151+
node: {
152+
__typename: "User",
153+
id: "user-1",
154+
createdAt: date.toISOString(),
155+
},
156+
},
157+
{
158+
__typename: "UserEdge",
159+
node: {
160+
__typename: "User",
161+
id: "user-2",
162+
createdAt: null,
163+
},
164+
},
165+
{
166+
__typename: "UserEdge",
167+
node: {
168+
__typename: "User",
169+
id: "user-3",
170+
createdAt: date.toISOString(),
171+
},
172+
},
173+
],
174+
},
175+
},
176+
graphqlErrors: [{ path: ["members", "edges", 1, "node", "createdAt"] }],
177+
});
178+
179+
t.render(test_catch("TestMembers"));
180+
await t.screen.findByText(
181+
"User: user-1 - 2025-01-01, Error!, User: user-3 - 2025-01-01"
182+
);
183+
});
184+
});
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
module QueryLoggedInUserProp = %relay(`
2+
query TestCatchLoggedInUserPropQuery {
3+
loggedInUser {
4+
createdAt @catch
5+
...TestCatchUser_user
6+
}
7+
}
8+
`)
9+
10+
module LoggedInUserFragment = %relay(`
11+
fragment TestCatchUser_user on User @catch {
12+
createdAt
13+
}
14+
`)
15+
16+
module TestLoggedInUserProp = {
17+
@react.component
18+
let make = () => {
19+
let query = QueryLoggedInUserProp.use(~variables=())
20+
21+
switch query.loggedInUser.createdAt {
22+
| Ok({value: createdAt}) =>
23+
<div>
24+
{React.string(
25+
"Got createdAt: " ++ createdAt->Js.Date.toISOString->Js.String2.slice(~from=0, ~to_=10),
26+
)}
27+
</div>
28+
| Error(_) => <div> {React.string("Error!")} </div>
29+
}
30+
}
31+
}
32+
33+
module TestLoggedInUserPropFragmentData = {
34+
@react.component
35+
let make = () => {
36+
let query = QueryLoggedInUserProp.use(~variables=())
37+
let fragmentData = LoggedInUserFragment.use(query.loggedInUser.fragmentRefs)
38+
39+
switch fragmentData {
40+
| Ok({value: {createdAt}}) =>
41+
<div>
42+
{React.string(
43+
"Got createdAt: " ++ createdAt->Js.Date.toISOString->Js.String2.slice(~from=0, ~to_=10),
44+
)}
45+
</div>
46+
| Error(_) => <div> {React.string("Error!")} </div>
47+
}
48+
}
49+
}
50+
51+
module QueryMember = %relay(`
52+
query TestCatchMemberPropQuery {
53+
member(id: "123") @catch {
54+
... on User {
55+
id
56+
createdAt
57+
}
58+
}
59+
}
60+
`)
61+
62+
module TestMember = {
63+
@react.component
64+
let make = () => {
65+
let query = QueryMember.use(~variables=())
66+
67+
switch query.member {
68+
| Ok({value: User({id, createdAt})}) =>
69+
<div>
70+
{React.string(
71+
"Got user id: " ++
72+
id ++
73+
", and createdAt: " ++
74+
createdAt->Js.Date.toISOString->Js.String2.slice(~from=0, ~to_=10),
75+
)}
76+
</div>
77+
| Error(_) => <div> {React.string("Error!")} </div>
78+
| _ => React.null
79+
}
80+
}
81+
}
82+
83+
module QueryMemberNested = %relay(`
84+
query TestCatchMemberPropNestedQuery {
85+
member(id: "123") {
86+
... on User {
87+
id
88+
memberOfSingular @catch {
89+
... on User {
90+
id
91+
createdAt
92+
}
93+
}
94+
}
95+
}
96+
}
97+
`)
98+
99+
module TestMemberNested = {
100+
@react.component
101+
let make = () => {
102+
let query = QueryMemberNested.use(~variables=())
103+
104+
switch query.member {
105+
| Some(User({id, memberOfSingular: Ok({value: User({createdAt})})})) =>
106+
<div>
107+
{React.string(
108+
"Got user id: " ++
109+
id ++
110+
", and createdAt: " ++
111+
createdAt->Js.Date.toISOString->Js.String2.slice(~from=0, ~to_=10),
112+
)}
113+
</div>
114+
| Some(User({memberOfSingular: Error(_)})) => <div> {React.string("Error nested!")} </div>
115+
| _ => React.null
116+
}
117+
}
118+
}
119+
120+
module QueryMembers = %relay(`
121+
query TestCatchMembersPropQuery {
122+
members(groupId: "123") {
123+
edges {
124+
node @catch {
125+
... on User {
126+
id
127+
createdAt
128+
}
129+
}
130+
}
131+
}
132+
}
133+
`)
134+
135+
module TestMembers = {
136+
@react.component
137+
let make = () => {
138+
let query = QueryMembers.use(~variables=())
139+
140+
let members =
141+
query.members
142+
->Belt.Option.flatMap(v => v.edges)
143+
->Belt.Option.getWithDefault([])
144+
->Belt.Array.keepMap(x => x->Belt.Option.map(r => r.node))
145+
146+
members
147+
->Js.Array2.map(r =>
148+
switch r {
149+
| Ok({value: User({id, createdAt})}) =>
150+
`User: ${id} - ${createdAt->Js.Date.toISOString->Js.String2.slice(~from=0, ~to_=10)}`
151+
| _ => "Error!"
152+
}
153+
)
154+
->Js.Array2.joinWith(", ")
155+
->React.string
156+
}
157+
}
158+
159+
@live
160+
let test_catch = testName => {
161+
let network = RescriptRelay.Network.makePromiseBased(~fetchFunction=RelayEnv.fetchQuery)
162+
163+
let environment = RescriptRelay.Environment.make(
164+
~network,
165+
~store=RescriptRelay.Store.make(~source=RescriptRelay.RecordSource.make()),
166+
)
167+
168+
<TestProviders.Wrapper environment>
169+
{switch testName {
170+
| "TestLoggedInUserProp" => <TestLoggedInUserProp />
171+
| "TestLoggedInUserPropFragmentData" => <TestLoggedInUserPropFragmentData />
172+
| "TestMember" => <TestMember />
173+
| "TestMemberNested" => <TestMemberNested />
174+
| "TestMembers" => <TestMembers />
175+
| _ => React.null
176+
}}
177+
</TestProviders.Wrapper>
178+
}

packages/rescript-relay/__tests__/TestsUtils.res

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ exception Malformed_date
22
exception Malformed_number
33

44
module Datetime = {
5+
@editor.completeFrom(Js.Date)
56
type t = Js.Date.t
7+
68
let parse = t =>
79
switch t->Js.Json.decodeString {
810
| None => raise(Malformed_date)

0 commit comments

Comments
 (0)