Skip to content

fix(integrations): Ensure httpclient integration works with Request #7786

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as Sentry from '@sentry/browser';
import { HttpClient } from '@sentry/integrations';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://[email protected]/1337',
integrations: [new HttpClient()],
tracesSampleRate: 1,
sendDefaultPii: true,
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/types';

import { sentryTest } from '../../../../utils/fixtures';
import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers';
import { sentryTest } from '../../../../../utils/fixtures';
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers';

sentryTest(
'should assign request and response context from a failed 500 fetch request',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const request = new Request('http://localhost:7654/foo', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cache: 'no-cache',
},
body: JSON.stringify({ test: true }),
});

fetch(request);
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/types';

import { sentryTest } from '../../../../../utils/fixtures';
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers';

sentryTest('works with a Request passed in', async ({ getLocalTestPath, page }) => {
const url = await getLocalTestPath({ testDir: __dirname });

await page.route('**/foo', route => {
return route.fulfill({
status: 500,
body: JSON.stringify({
error: {
message: 'Internal Server Error',
},
}),
headers: {
'Content-Type': 'text/html',
},
});
});

const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);

expect(eventData.exception?.values).toHaveLength(1);

// Not able to get the cookies from the request/response because of Playwright bug
// https://github.com/microsoft/playwright/issues/11035
expect(eventData).toMatchObject({
message: 'HTTP Client Error with status code: 500',
exception: {
values: [
{
type: 'Error',
value: 'HTTP Client Error with status code: 500',
mechanism: {
type: 'http.client',
handled: true,
},
},
],
},
request: {
url: 'http://localhost:7654/foo',
method: 'POST',
headers: {
accept: 'application/json',
cache: 'no-cache',
'content-type': 'application/json',
},
},
contexts: {
response: {
status_code: 500,
body_size: 45,
headers: {
'content-type': 'text/html',
'content-length': '45',
},
},
},
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const request = new Request('http://localhost:7654/foo', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cache: 'no-cache',
},
body: JSON.stringify({ test: true }),
});

fetch(request, {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cache: 'cache',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/types';

import { sentryTest } from '../../../../../utils/fixtures';
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers';

sentryTest(
'works with a Request (with body) & options passed in - handling used body',
async ({ getLocalTestPath, page }) => {
const url = await getLocalTestPath({ testDir: __dirname });

await page.route('**/foo', route => {
return route.fulfill({
status: 500,
body: JSON.stringify({
error: {
message: 'Internal Server Error',
},
}),
headers: {
'Content-Type': 'text/html',
},
});
});

const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);

expect(eventData.exception?.values).toHaveLength(1);

// Not able to get the cookies from the request/response because of Playwright bug
// https://github.com/microsoft/playwright/issues/11035
expect(eventData).toMatchObject({
message: 'HTTP Client Error with status code: 500',
exception: {
values: [
{
type: 'Error',
value: 'HTTP Client Error with status code: 500',
mechanism: {
type: 'http.client',
handled: true,
},
},
],
},
request: {
url: 'http://localhost:7654/foo',
method: 'POST',
headers: {
accept: 'application/json',
cache: 'no-cache',
'content-type': 'application/json',
},
},
contexts: {
response: {
status_code: 500,
body_size: 45,
headers: {
'content-type': 'text/html',
'content-length': '45',
},
},
},
});
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const request = new Request('http://localhost:7654/foo', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cache: 'no-cache',
},
});

fetch(request, {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cache: 'cache',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/types';

import { sentryTest } from '../../../../../utils/fixtures';
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers';

sentryTest('works with a Request (without body) & options passed in', async ({ getLocalTestPath, page }) => {
const url = await getLocalTestPath({ testDir: __dirname });

await page.route('**/foo', route => {
return route.fulfill({
status: 500,
body: JSON.stringify({
error: {
message: 'Internal Server Error',
},
}),
headers: {
'Content-Type': 'text/html',
},
});
});

const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);

expect(eventData.exception?.values).toHaveLength(1);

// Not able to get the cookies from the request/response because of Playwright bug
// https://github.com/microsoft/playwright/issues/11035
expect(eventData).toMatchObject({
message: 'HTTP Client Error with status code: 500',
exception: {
values: [
{
type: 'Error',
value: 'HTTP Client Error with status code: 500',
mechanism: {
type: 'http.client',
handled: true,
},
},
],
},
request: {
url: 'http://localhost:7654/foo',
method: 'POST',
headers: {
accept: 'application/json',
cache: 'cache',
'content-type': 'application/json',
},
},
contexts: {
response: {
status_code: 500,
body_size: 45,
headers: {
'content-type': 'text/html',
'content-length': '45',
},
},
},
});
});
2 changes: 1 addition & 1 deletion packages/integration-shims/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"build:dev": "yarn build",
"build:watch": "run-p build:transpile:watch build:types:watch",
"build:dev:watch": "run-p build:watch",
"build:transpile:watch": "yarn build:rollup --watch",
"build:transpile:watch": "yarn build:transpile --watch",
"build:types:watch": "yarn build:types --watch",
"clean": "rimraf build",
"fix": "run-s fix:eslint fix:prettier",
Expand Down
17 changes: 16 additions & 1 deletion packages/integrations/src/httpclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class HttpClient implements Integration {
*/
private _fetchResponseHandler(requestInfo: RequestInfo, response: Response, requestInit?: RequestInit): void {
if (this._getCurrentHub && this._shouldCaptureResponse(response.status, response.url)) {
const request = new Request(requestInfo, requestInit);
const request = _getRequest(requestInfo, requestInit);
const hub = this._getCurrentHub();

let requestHeaders, responseHeaders, requestCookies, responseCookies;
Expand Down Expand Up @@ -417,3 +417,18 @@ export class HttpClient implements Integration {
return event;
}
}

function _getRequest(requestInfo: RequestInfo, requestInit?: RequestInit): Request {
if (!requestInit && requestInfo instanceof Request) {
return requestInfo;
}

// If both are set, we try to construct a new Request with the given arguments
// However, if e.g. the original request has a `body`, this will throw an error because it was already accessed
// In this case, as a fallback, we just use the original request - using both is rather an edge case
if (requestInfo instanceof Request && requestInfo.bodyUsed) {
return requestInfo;
}

return new Request(requestInfo, requestInit);
}
2 changes: 1 addition & 1 deletion packages/replay-worker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"build:dev": "yarn build",
"build:watch": "run-p build:transpile:watch build:types:watch",
"build:dev:watch": "run-p build:watch",
"build:transpile:watch": "yarn build:rollup --watch",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this about?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just noticed this while working here that this is actually referencing wrong commands, so fixed them while at it! same for integration-shims, copy paste error 😅

"build:transpile:watch": "yarn build:transpile --watch",
"build:types:watch": "yarn build:types --watch",
"clean": "rimraf build",
"fix": "run-s fix:eslint fix:prettier",
Expand Down