Skip to content

Commit caecb78

Browse files
committed
feat(tracing): allow direct pg module to enable esbuild support
1 parent bb67a11 commit caecb78

File tree

2 files changed

+46
-16
lines changed

2 files changed

+46
-16
lines changed

packages/tracing-internal/src/node/integrations/postgres.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@ import { fill, isThenable, loadModule, logger } from '@sentry/utils';
55
import type { LazyLoadedIntegration } from './lazy';
66
import { shouldDisableAutoInstrumentation } from './utils/node-utils';
77

8+
type PgClientQuery = (
9+
config: unknown,
10+
values?: unknown,
11+
callback?: (err: unknown, result: unknown) => void,
12+
) => void | Promise<unknown>;
13+
814
interface PgClient {
915
prototype: {
10-
query: () => void | Promise<unknown>;
16+
query: PgClientQuery;
1117
};
1218
}
1319

@@ -18,12 +24,17 @@ interface PgClientThis {
1824
user?: string;
1925
}
2026

27+
type PGModule = { Client: PgClient; native: { Client: PgClient } };
28+
2129
interface PgOptions {
2230
usePgNative?: boolean;
31+
/**
32+
* Supply your postgres module directly, instead of having Sentry attempt automatic resolution.
33+
* Use this if you (a) use a module that's not `pg`, or (b) use a bundler that breaks resolution (e.g. esbuild).
34+
*/
35+
module?: PGModule;
2336
}
2437

25-
type PGModule = { Client: PgClient; native: { Client: PgClient } };
26-
2738
/** Tracing integration for node-postgres package */
2839
export class Postgres implements LazyLoadedIntegration<PGModule> {
2940
/**
@@ -43,6 +54,7 @@ export class Postgres implements LazyLoadedIntegration<PGModule> {
4354
public constructor(options: PgOptions = {}) {
4455
this.name = Postgres.id;
4556
this._usePgNative = !!options.usePgNative;
57+
this._module = options.module;
4658
}
4759

4860
/** @inheritdoc */
@@ -80,7 +92,7 @@ export class Postgres implements LazyLoadedIntegration<PGModule> {
8092
* function (query, params) => Promise
8193
* function (pg.Cursor) => pg.Cursor
8294
*/
83-
fill(Client.prototype, 'query', function (orig: () => void | Promise<unknown>) {
95+
fill(Client.prototype, 'query', function (orig: PgClientQuery) {
8496
return function (this: PgClientThis, config: unknown, values: unknown, callback: unknown) {
8597
const scope = getCurrentHub().getScope();
8698
const parentSpan = scope.getSpan();

packages/tracing/test/integrations/node/postgres.test.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
/* eslint-disable deprecation/deprecation */
22
/* eslint-disable @typescript-eslint/unbound-method */
33
import { Hub, Scope } from '@sentry/core';
4-
import { logger } from '@sentry/utils';
4+
import { logger, loadModule } from '@sentry/utils';
55

66
import { Integrations, Span } from '../../../src';
77
import { getTestClient } from '../../testutils';
88

99
class PgClient {
1010
// https://node-postgres.com/api/client#clientquery
11-
public query(_text: unknown, values: unknown, callback?: () => void) {
11+
public query(_text: unknown, values: unknown, callback?: (err: unknown, result: unknown) => void) {
1212
if (typeof callback === 'function') {
13-
callback();
13+
callback(null, null);
1414
return;
1515
}
1616

@@ -25,25 +25,28 @@ class PgClient {
2525

2626
// Jest mocks get hoisted. vars starting with `mock` are hoisted before imports.
2727
/* eslint-disable no-var */
28-
var mockClient = PgClient;
28+
var mockModule = {
29+
Client: PgClient,
30+
native: {
31+
Client: PgClient,
32+
},
33+
};
2934

3035
// mock for 'pg' / 'pg-native' package
3136
jest.mock('@sentry/utils', () => {
3237
const actual = jest.requireActual('@sentry/utils');
3338
return {
3439
...actual,
35-
loadModule() {
36-
return {
37-
Client: mockClient,
38-
native: {
39-
Client: mockClient,
40-
},
41-
};
42-
},
40+
loadModule: jest.fn(() => mockModule),
4341
};
4442
});
4543

4644
describe('setupOnce', () => {
45+
beforeEach(() => {
46+
jest.clearAllMocks();
47+
jest.resetAllMocks();
48+
});
49+
4750
['pg', 'pg-native'].forEach(pgApi => {
4851
const Client: PgClient = new PgClient();
4952
let scope = new Scope();
@@ -127,4 +130,19 @@ describe('setupOnce', () => {
127130

128131
expect(loggerLogSpy).toBeCalledWith('Postgres Integration is skipped because of instrumenter configuration.');
129132
});
133+
134+
it('does not attempt resolution when module is passed directly', async () => {
135+
const scope = new Scope();
136+
jest.spyOn(scope, 'getSpan').mockReturnValueOnce(new Span());
137+
138+
new Integrations.Postgres({ module: mockModule }).setupOnce(
139+
() => undefined,
140+
() => new Hub(undefined, scope),
141+
);
142+
143+
await new PgClient().query('SELECT NOW()', null);
144+
145+
expect(loadModule).not.toBeCalled();
146+
expect(scope.getSpan).toBeCalled();
147+
});
130148
});

0 commit comments

Comments
 (0)