Skip to content

Commit f3c0155

Browse files
committed
fix(remix): Support merging json responses from root loader functions.
1 parent 487810a commit f3c0155

File tree

4 files changed

+228
-3
lines changed

4 files changed

+228
-3
lines changed

packages/remix/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@
6161
"lint:prettier": "prettier --check \"{src,test,scripts}/**/*.ts\"",
6262
"test": "run-s test:unit",
6363
"test:integration": "run-s test:integration:prepare test:integration:client test:integration:server",
64+
"test:integration:clean": "(cd test/integration && rimraf .cache build node_modules)",
6465
"test:integration:ci": "run-s test:integration:prepare test:integration:client:ci test:integration:server",
65-
"test:integration:prepare": "(cd test/integration && yarn)",
66+
"test:integration:prepare": "yarn test:integration:clean && (cd test/integration && yarn)",
6667
"test:integration:client": "yarn playwright install-deps && yarn playwright test test/integration/test/client/",
6768
"test:integration:client:ci": "yarn test:integration:client --browser='all' --reporter='line'",
6869
"test:integration:server": "jest --config=test/integration/jest.config.js test/integration/test/server/",

packages/remix/src/utils/instrumentServer.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,18 @@ function getTraceAndBaggage(): { sentryTrace?: string; sentryBaggage?: string }
262262
function makeWrappedRootLoader(origLoader: DataFunction): DataFunction {
263263
return async function (this: unknown, args: DataFunctionArgs): Promise<Response | AppData> {
264264
const res = await origLoader.call(this, args);
265+
const traceAndBaggage = getTraceAndBaggage();
265266

266-
return { ...res, ...getTraceAndBaggage() };
267+
if (isResponse(res)) {
268+
const resClone = res.clone();
269+
const data = await extractData(resClone);
270+
271+
if (typeof data === 'object') {
272+
return { ...data, ...traceAndBaggage };
273+
}
274+
}
275+
276+
return { ...res, ...traceAndBaggage };
267277
};
268278
}
269279

packages/remix/test/integration/app/root.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { MetaFunction } from '@remix-run/node';
1+
import { MetaFunction, LoaderFunction, json, redirect } from '@remix-run/node';
22
import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react';
33
import { withSentry } from '@sentry/remix';
44

@@ -10,6 +10,33 @@ export const meta: MetaFunction = ({ data }) => ({
1010
baggage: data.sentryBaggage,
1111
});
1212

13+
export const loader: LoaderFunction = async ({ request }) => {
14+
const url = new URL(request.url);
15+
const type = url.searchParams.get('type');
16+
17+
switch (type) {
18+
case 'empty':
19+
return {};
20+
case 'plain':
21+
return {
22+
data_one: [],
23+
data_two: 'a string',
24+
};
25+
case 'json':
26+
return json({ data_one: [], data_two: 'a string' }, { headers: { 'Cache-Control': 'max-age=300' } });
27+
case 'null':
28+
return null;
29+
case 'undefined':
30+
return undefined;
31+
case 'throw-redirect':
32+
throw redirect('/plain');
33+
case 'return-redirect':
34+
return redirect('/plain');
35+
case 'return-redirect-json':
36+
return redirect('/json');
37+
}
38+
};
39+
1340
function App() {
1441
return (
1542
<html lang="en">
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import { test, expect, Page } from '@playwright/test';
2+
3+
export function getRouteData(page: Page): Promise<any> {
4+
return page.evaluate('window.__remixContext.routeData').catch(err => {
5+
return {};
6+
});
7+
}
8+
9+
test('should inject `sentry-trace` and `baggage` into root loader returning `{}`.', async ({ page }) => {
10+
await page.goto('/?type=empty');
11+
const sentryTraceTag = await page.$('meta[name="sentry-trace"]');
12+
const sentryTraceContent = await sentryTraceTag?.getAttribute('content');
13+
14+
expect(sentryTraceContent).toEqual(expect.any(String));
15+
16+
const sentryBaggageTag = await page.$('meta[name="baggage"]');
17+
const sentryBaggageContent = await sentryBaggageTag?.getAttribute('content');
18+
19+
expect(sentryBaggageContent).toEqual(expect.any(String));
20+
21+
const rootData = (await getRouteData(page))['root'];
22+
23+
expect(rootData).toMatchObject({
24+
sentryTrace: sentryTraceContent,
25+
sentryBaggage: sentryBaggageContent,
26+
});
27+
});
28+
29+
test('should inject `sentry-trace` and `baggage` into root loader returning a plain object.', async ({ page }) => {
30+
await page.goto('/?type=plain');
31+
const sentryTraceTag = await page.$('meta[name="sentry-trace"]');
32+
const sentryTraceContent = await sentryTraceTag?.getAttribute('content');
33+
34+
expect(sentryTraceContent).toEqual(expect.any(String));
35+
36+
const sentryBaggageTag = await page.$('meta[name="baggage"]');
37+
const sentryBaggageContent = await sentryBaggageTag?.getAttribute('content');
38+
39+
expect(sentryBaggageContent).toEqual(expect.any(String));
40+
41+
const rootData = (await getRouteData(page))['root'];
42+
43+
expect(rootData).toMatchObject({
44+
data_one: [],
45+
data_two: 'a string',
46+
sentryTrace: sentryTraceContent,
47+
sentryBaggage: sentryBaggageContent,
48+
});
49+
});
50+
51+
test('should inject `sentry-trace` and `baggage` into root loader returning a `JSON response`.', async ({ page }) => {
52+
await page.goto('/?type=json');
53+
54+
const sentryTraceTag = await page.$('meta[name="sentry-trace"]');
55+
const sentryTraceContent = await sentryTraceTag?.getAttribute('content');
56+
57+
expect(sentryTraceContent).toEqual(expect.any(String));
58+
59+
const sentryBaggageTag = await page.$('meta[name="baggage"]');
60+
const sentryBaggageContent = await sentryBaggageTag?.getAttribute('content');
61+
62+
expect(sentryBaggageContent).toEqual(expect.any(String));
63+
64+
const rootData = (await getRouteData(page))['root'];
65+
66+
expect(rootData).toMatchObject({
67+
data_one: [],
68+
data_two: 'a string',
69+
sentryTrace: sentryTraceContent,
70+
sentryBaggage: sentryBaggageContent,
71+
});
72+
});
73+
74+
test('should inject `sentry-trace` and `baggage` into root loader returning `null`.', async ({ page }) => {
75+
await page.goto('/?type=null');
76+
77+
const sentryTraceTag = await page.$('meta[name="sentry-trace"]');
78+
const sentryTraceContent = await sentryTraceTag?.getAttribute('content');
79+
80+
expect(sentryTraceContent).toEqual(expect.any(String));
81+
82+
const sentryBaggageTag = await page.$('meta[name="baggage"]');
83+
const sentryBaggageContent = await sentryBaggageTag?.getAttribute('content');
84+
85+
expect(sentryBaggageContent).toEqual(expect.any(String));
86+
87+
const rootData = (await getRouteData(page))['root'];
88+
89+
expect(rootData).toMatchObject({
90+
sentryTrace: sentryTraceContent,
91+
sentryBaggage: sentryBaggageContent,
92+
});
93+
});
94+
95+
test('should inject `sentry-trace` and `baggage` into root loader returning `undefined`.', async ({ page }) => {
96+
await page.goto('/?type=undefined');
97+
98+
const sentryTraceTag = await page.$('meta[name="sentry-trace"]');
99+
const sentryTraceContent = await sentryTraceTag?.getAttribute('content');
100+
101+
expect(sentryTraceContent).toEqual(expect.any(String));
102+
103+
const sentryBaggageTag = await page.$('meta[name="baggage"]');
104+
const sentryBaggageContent = await sentryBaggageTag?.getAttribute('content');
105+
106+
expect(sentryBaggageContent).toEqual(expect.any(String));
107+
108+
const rootData = (await getRouteData(page))['root'];
109+
110+
debugger;
111+
expect(rootData).toMatchObject({
112+
sentryTrace: sentryTraceContent,
113+
sentryBaggage: sentryBaggageContent,
114+
});
115+
});
116+
117+
test('should inject `sentry-trace` and `baggage` into root loader throwing a redirection to a plain object.', async ({
118+
page,
119+
}) => {
120+
await page.goto('/?type="throw-redirect"');
121+
122+
const sentryTraceTag = await page.$('meta[name="sentry-trace"]');
123+
const sentryTraceContent = await sentryTraceTag?.getAttribute('content');
124+
125+
expect(sentryTraceContent).toEqual(expect.any(String));
126+
127+
const sentryBaggageTag = await page.$('meta[name="baggage"]');
128+
const sentryBaggageContent = await sentryBaggageTag?.getAttribute('content');
129+
130+
expect(sentryBaggageContent).toEqual(expect.any(String));
131+
132+
const rootData = (await getRouteData(page))['root'];
133+
134+
debugger;
135+
expect(rootData).toMatchObject({
136+
sentryTrace: sentryTraceContent,
137+
sentryBaggage: sentryBaggageContent,
138+
});
139+
});
140+
141+
test('should inject `sentry-trace` and `baggage` into root loader returning a redirection to a plain object', async ({
142+
page,
143+
}) => {
144+
await page.goto('/?type="return-redirect"');
145+
146+
const sentryTraceTag = await page.$('meta[name="sentry-trace"]');
147+
const sentryTraceContent = await sentryTraceTag?.getAttribute('content');
148+
149+
expect(sentryTraceContent).toEqual(expect.any(String));
150+
151+
const sentryBaggageTag = await page.$('meta[name="baggage"]');
152+
const sentryBaggageContent = await sentryBaggageTag?.getAttribute('content');
153+
154+
expect(sentryBaggageContent).toEqual(expect.any(String));
155+
156+
const rootData = (await getRouteData(page))['root'];
157+
158+
debugger;
159+
expect(rootData).toMatchObject({
160+
sentryTrace: sentryTraceContent,
161+
sentryBaggage: sentryBaggageContent,
162+
});
163+
});
164+
165+
test('should inject `sentry-trace` and `baggage` into root loader returning a redirection to a `JSON response`', async ({
166+
page,
167+
}) => {
168+
await page.goto('/?type="return-redirect"');
169+
170+
const sentryTraceTag = await page.$('meta[name="sentry-trace"]');
171+
const sentryTraceContent = await sentryTraceTag?.getAttribute('content');
172+
173+
expect(sentryTraceContent).toEqual(expect.any(String));
174+
175+
const sentryBaggageTag = await page.$('meta[name="baggage"]');
176+
const sentryBaggageContent = await sentryBaggageTag?.getAttribute('content');
177+
178+
expect(sentryBaggageContent).toEqual(expect.any(String));
179+
180+
const rootData = (await getRouteData(page))['root'];
181+
182+
debugger;
183+
expect(rootData).toMatchObject({
184+
sentryTrace: sentryTraceContent,
185+
sentryBaggage: sentryBaggageContent,
186+
});
187+
});

0 commit comments

Comments
 (0)