Skip to content

Commit c4fd019

Browse files
AbhiPrasadonurtemizkan
authored andcommitted
feat(core): Deprecate API class (#4281)
1 parent 672ea01 commit c4fd019

File tree

7 files changed

+141
-93
lines changed

7 files changed

+141
-93
lines changed

Diff for: packages/browser/src/transports/base.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { API } from '@sentry/core';
1+
import {
2+
APIDetails,
3+
getEnvelopeEndpointWithUrlEncodedAuth,
4+
getStoreEndpointWithUrlEncodedAuth,
5+
initAPIDetails,
6+
} from '@sentry/core';
27
import {
38
Event,
49
Outcome,
@@ -38,7 +43,7 @@ export abstract class BaseTransport implements Transport {
3843
public url: string;
3944

4045
/** Helper to get Sentry API endpoints. */
41-
protected readonly _api: API;
46+
protected readonly _api: APIDetails;
4247

4348
/** A simple buffer holding all requests. */
4449
protected readonly _buffer: PromiseBuffer<SentryResponse> = new PromiseBuffer(30);
@@ -49,9 +54,9 @@ export abstract class BaseTransport implements Transport {
4954
protected _outcomes: { [key: string]: number } = {};
5055

5156
public constructor(public options: TransportOptions) {
52-
this._api = new API(options.dsn, options._metadata, options.tunnel);
57+
this._api = initAPIDetails(options.dsn, options._metadata, options.tunnel);
5358
// eslint-disable-next-line deprecation/deprecation
54-
this.url = this._api.getStoreEndpointWithUrlEncodedAuth();
59+
this.url = getStoreEndpointWithUrlEncodedAuth(this._api.dsn);
5560

5661
if (this.options.sendClientReports && global.document) {
5762
global.document.addEventListener('visibilitychange', () => {
@@ -112,9 +117,9 @@ export abstract class BaseTransport implements Transport {
112117

113118
logger.log(`Flushing outcomes:\n${JSON.stringify(outcomes, null, 2)}`);
114119

115-
const url = this._api.getEnvelopeEndpointWithUrlEncodedAuth();
120+
const url = getEnvelopeEndpointWithUrlEncodedAuth(this._api.dsn, this._api.tunnel);
116121
// Envelope header is required to be at least an empty object
117-
const envelopeHeader = JSON.stringify({ ...(this.options.tunnel && { dsn: this._api.getDsn().toString() }) });
122+
const envelopeHeader = JSON.stringify({ ...(this._api.tunnel && { dsn: this._api.dsn.toString() }) });
118123
const itemHeaders = JSON.stringify({
119124
type: 'client_report',
120125
});

Diff for: packages/core/src/api.ts

+90-60
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,26 @@ import { Dsn, urlEncode } from '@sentry/utils';
33

44
const SENTRY_API_VERSION = '7';
55

6+
/**
7+
* Stores details about a Sentry SDK
8+
*/
9+
export interface APIDetails {
10+
/** The DSN as passed to Sentry.init() */
11+
initDsn: DsnLike;
12+
/** Metadata about the SDK (name, version, etc) for inclusion in envelope headers */
13+
metadata: SdkMetadata;
14+
/** The internally used Dsn object. */
15+
readonly dsn: Dsn;
16+
/** The envelope tunnel to use. */
17+
readonly tunnel?: string;
18+
}
19+
620
/**
721
* Helper class to provide urls, headers and metadata that can be used to form
822
* different types of requests to Sentry endpoints.
923
* Supports both envelopes and regular event requests.
24+
*
25+
* @deprecated Please use APIDetails
1026
**/
1127
export class API {
1228
/** The DSN as passed to Sentry.init() */
@@ -41,13 +57,12 @@ export class API {
4157

4258
/** Returns the prefix to construct Sentry ingestion API endpoints. */
4359
public getBaseApiEndpoint(): string {
44-
const dsn = this.getDsn();
45-
return getBaseApiEndpoint(dsn);
60+
return getBaseApiEndpoint(this._dsnObject);
4661
}
4762

4863
/** Returns the store endpoint URL. */
4964
public getStoreEndpoint(): string {
50-
return this._getIngestEndpoint('store');
65+
return getStoreEndpoint(this._dsnObject);
5166
}
5267

5368
/**
@@ -56,7 +71,7 @@ export class API {
5671
* Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests.
5772
*/
5873
public getStoreEndpointWithUrlEncodedAuth(): string {
59-
return `${this.getStoreEndpoint()}?${this._encodedAuth()}`;
74+
return getStoreEndpointWithUrlEncodedAuth(this._dsnObject);
6075
}
6176

6277
/**
@@ -65,64 +80,18 @@ export class API {
6580
* Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests.
6681
*/
6782
public getEnvelopeEndpointWithUrlEncodedAuth(): string {
68-
if (this.forceEnvelope()) {
69-
return this._tunnel as string;
70-
}
71-
72-
return `${this._getEnvelopeEndpoint()}?${this._encodedAuth()}`;
73-
}
74-
75-
/** Returns only the path component for the store endpoint. */
76-
public getStoreEndpointPath(): string {
77-
const dsn = this.getDsn();
78-
return `${dsn.path ? `/${dsn.path}` : ''}/api/${dsn.projectId}/store/`;
79-
}
80-
81-
/**
82-
* Returns an object that can be used in request headers.
83-
* This is needed for node and the old /store endpoint in sentry
84-
*/
85-
public getRequestHeaders(clientName: string, clientVersion: string): { [key: string]: string } {
86-
// CHANGE THIS to use metadata but keep clientName and clientVersion compatible
87-
const dsn = this.getDsn();
88-
const header = [`Sentry sentry_version=${SENTRY_API_VERSION}`];
89-
header.push(`sentry_client=${clientName}/${clientVersion}`);
90-
header.push(`sentry_key=${dsn.publicKey}`);
91-
if (dsn.pass) {
92-
header.push(`sentry_secret=${dsn.pass}`);
93-
}
94-
return {
95-
'Content-Type': 'application/json',
96-
'X-Sentry-Auth': header.join(', '),
97-
};
98-
}
99-
100-
/** Returns the envelope endpoint URL. */
101-
private _getEnvelopeEndpoint(): string {
102-
return this._getIngestEndpoint('envelope');
103-
}
104-
105-
/** Returns the ingest API endpoint for target. */
106-
private _getIngestEndpoint(target: 'store' | 'envelope'): string {
107-
if (this._tunnel) {
108-
return this._tunnel;
109-
}
110-
const base = this.getBaseApiEndpoint();
111-
const dsn = this.getDsn();
112-
return `${base}${dsn.projectId}/${target}/`;
83+
return getEnvelopeEndpointWithUrlEncodedAuth(this._dsnObject, this._tunnel);
11384
}
85+
}
11486

115-
/** Returns a URL-encoded string with auth config suitable for a query string. */
116-
private _encodedAuth(): string {
117-
const dsn = this.getDsn();
118-
const auth = {
119-
// We send only the minimum set of required information. See
120-
// https://github.com/getsentry/sentry-javascript/issues/2572.
121-
sentry_key: dsn.publicKey,
122-
sentry_version: SENTRY_API_VERSION,
123-
};
124-
return urlEncode(auth);
125-
}
87+
/** Initializes API Details */
88+
export function initAPIDetails(dsn: DsnLike, metadata?: SdkMetadata, tunnel?: string): APIDetails {
89+
return {
90+
initDsn: dsn,
91+
metadata: metadata || {},
92+
dsn: new Dsn(dsn),
93+
tunnel,
94+
} as APIDetails;
12695
}
12796

12897
/** Returns the prefix to construct Sentry ingestion API endpoints. */
@@ -132,6 +101,67 @@ function getBaseApiEndpoint(dsn: Dsn): string {
132101
return `${protocol}//${dsn.host}${port}${dsn.path ? `/${dsn.path}` : ''}/api/`;
133102
}
134103

104+
/** Returns the ingest API endpoint for target. */
105+
function _getIngestEndpoint(dsn: Dsn, target: 'store' | 'envelope'): string {
106+
return `${getBaseApiEndpoint(dsn)}${dsn.projectId}/${target}/`;
107+
}
108+
109+
/** Returns a URL-encoded string with auth config suitable for a query string. */
110+
function _encodedAuth(dsn: Dsn): string {
111+
return urlEncode({
112+
// We send only the minimum set of required information. See
113+
// https://github.com/getsentry/sentry-javascript/issues/2572.
114+
sentry_key: dsn.publicKey,
115+
sentry_version: SENTRY_API_VERSION,
116+
});
117+
}
118+
119+
/** Returns the store endpoint URL. */
120+
function getStoreEndpoint(dsn: Dsn): string {
121+
return _getIngestEndpoint(dsn, 'store');
122+
}
123+
124+
/**
125+
* Returns the store endpoint URL with auth in the query string.
126+
*
127+
* Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests.
128+
*/
129+
export function getStoreEndpointWithUrlEncodedAuth(dsn: Dsn): string {
130+
return `${getStoreEndpoint(dsn)}?${_encodedAuth(dsn)}`;
131+
}
132+
133+
/** Returns the envelope endpoint URL. */
134+
function _getEnvelopeEndpoint(dsn: Dsn): string {
135+
return _getIngestEndpoint(dsn, 'envelope');
136+
}
137+
138+
/**
139+
* Returns the envelope endpoint URL with auth in the query string.
140+
*
141+
* Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests.
142+
*/
143+
export function getEnvelopeEndpointWithUrlEncodedAuth(dsn: Dsn, tunnel?: string): string {
144+
return tunnel ? tunnel : `${_getEnvelopeEndpoint(dsn)}?${_encodedAuth(dsn)}`;
145+
}
146+
147+
/**
148+
* Returns an object that can be used in request headers.
149+
* This is needed for node and the old /store endpoint in sentry
150+
*/
151+
export function getRequestHeaders(dsn: Dsn, clientName: string, clientVersion: string): { [key: string]: string } {
152+
// CHANGE THIS to use metadata but keep clientName and clientVersion compatible
153+
const header = [`Sentry sentry_version=${SENTRY_API_VERSION}`];
154+
header.push(`sentry_client=${clientName}/${clientVersion}`);
155+
header.push(`sentry_key=${dsn.publicKey}`);
156+
if (dsn.pass) {
157+
header.push(`sentry_secret=${dsn.pass}`);
158+
}
159+
return {
160+
'Content-Type': 'application/json',
161+
'X-Sentry-Auth': header.join(', '),
162+
};
163+
}
164+
135165
/** Returns the url to the report dialog endpoint. */
136166
export function getReportDialogEndpoint(
137167
dsnLike: DsnLike,

Diff for: packages/core/src/index.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,16 @@ export {
1414
withScope,
1515
} from '@sentry/minimal';
1616
export { addGlobalEventProcessor, getCurrentHub, getHubFromCarrier, Hub, makeMain, Scope } from '@sentry/hub';
17-
export { API, getReportDialogEndpoint } from './api';
17+
export {
18+
// eslint-disable-next-line deprecation/deprecation
19+
API,
20+
APIDetails,
21+
getEnvelopeEndpointWithUrlEncodedAuth,
22+
getStoreEndpointWithUrlEncodedAuth,
23+
getRequestHeaders,
24+
initAPIDetails,
25+
getReportDialogEndpoint,
26+
} from './api';
1827
export { BaseClient } from './baseclient';
1928
export { BackendClass, BaseBackend } from './basebackend';
2029
export { eventToSentryRequest, sessionToSentryRequest } from './request';

Diff for: packages/core/src/request.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Event, SdkInfo, SentryRequest, SentryRequestType, Session, SessionAggregates } from '@sentry/types';
22

3-
import { API } from './api';
3+
import { APIDetails, getEnvelopeEndpointWithUrlEncodedAuth, getStoreEndpointWithUrlEncodedAuth } from './api';
44

55
/** Extract sdk info from from the API metadata */
6-
function getSdkMetadataForEnvelopeHeader(api: API): SdkInfo | undefined {
6+
function getSdkMetadataForEnvelopeHeader(api: APIDetails): SdkInfo | undefined {
77
if (!api.metadata || !api.metadata.sdk) {
88
return;
99
}
@@ -28,12 +28,12 @@ function enhanceEventWithSdkInfo(event: Event, sdkInfo?: SdkInfo): Event {
2828
}
2929

3030
/** Creates a SentryRequest from a Session. */
31-
export function sessionToSentryRequest(session: Session | SessionAggregates, api: API): SentryRequest {
31+
export function sessionToSentryRequest(session: Session | SessionAggregates, api: APIDetails): SentryRequest {
3232
const sdkInfo = getSdkMetadataForEnvelopeHeader(api);
3333
const envelopeHeaders = JSON.stringify({
3434
sent_at: new Date().toISOString(),
3535
...(sdkInfo && { sdk: sdkInfo }),
36-
...(api.forceEnvelope() && { dsn: api.getDsn().toString() }),
36+
...(!!api.tunnel && { dsn: api.dsn.toString() }),
3737
});
3838
// I know this is hacky but we don't want to add `session` to request type since it's never rate limited
3939
const type: SentryRequestType = 'aggregates' in session ? ('sessions' as SentryRequestType) : 'session';
@@ -44,15 +44,15 @@ export function sessionToSentryRequest(session: Session | SessionAggregates, api
4444
return {
4545
body: `${envelopeHeaders}\n${itemHeaders}\n${JSON.stringify(session)}`,
4646
type,
47-
url: api.getEnvelopeEndpointWithUrlEncodedAuth(),
47+
url: getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel),
4848
};
4949
}
5050

5151
/** Creates a SentryRequest from an event. */
52-
export function eventToSentryRequest(event: Event, api: API): SentryRequest {
52+
export function eventToSentryRequest(event: Event, api: APIDetails): SentryRequest {
5353
const sdkInfo = getSdkMetadataForEnvelopeHeader(api);
5454
const eventType = event.type || 'event';
55-
const useEnvelope = eventType === 'transaction' || api.forceEnvelope();
55+
const useEnvelope = eventType === 'transaction' || !!api.tunnel;
5656

5757
const { transactionSampling, ...metadata } = event.debug_meta || {};
5858
const { method: samplingMethod, rate: sampleRate } = transactionSampling || {};
@@ -65,7 +65,9 @@ export function eventToSentryRequest(event: Event, api: API): SentryRequest {
6565
const req: SentryRequest = {
6666
body: JSON.stringify(sdkInfo ? enhanceEventWithSdkInfo(event, api.metadata.sdk) : event),
6767
type: eventType,
68-
url: useEnvelope ? api.getEnvelopeEndpointWithUrlEncodedAuth() : api.getStoreEndpointWithUrlEncodedAuth(),
68+
url: useEnvelope
69+
? getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel)
70+
: getStoreEndpointWithUrlEncodedAuth(api.dsn),
6971
};
7072

7173
// https://develop.sentry.dev/sdk/envelopes/
@@ -79,7 +81,7 @@ export function eventToSentryRequest(event: Event, api: API): SentryRequest {
7981
event_id: event.event_id,
8082
sent_at: new Date().toISOString(),
8183
...(sdkInfo && { sdk: sdkInfo }),
82-
...(api.forceEnvelope() && { dsn: api.getDsn().toString() }),
84+
...(!!api.tunnel && { dsn: api.dsn.toString() }),
8385
});
8486
const itemHeaders = JSON.stringify({
8587
type: eventType,

Diff for: packages/core/test/lib/api.test.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
/* eslint-disable deprecation/deprecation */
12
import { Dsn } from '@sentry/utils';
23

3-
import { API, getReportDialogEndpoint } from '../../src/api';
4+
import { API, getReportDialogEndpoint, getRequestHeaders } from '../../src/api';
45

56
const ingestDsn = 'https://[email protected]:1234/subpath/123';
67
const dsnPublic = 'https://[email protected]:1234/subpath/123';
@@ -24,12 +25,12 @@ describe('API', () => {
2425
});
2526

2627
test('getRequestHeaders', () => {
27-
expect(new API(dsnPublic).getRequestHeaders('a', '1.0')).toMatchObject({
28+
expect(getRequestHeaders(new Dsn(dsnPublic), 'a', '1.0')).toMatchObject({
2829
'Content-Type': 'application/json',
2930
'X-Sentry-Auth': expect.stringMatching(/^Sentry sentry_version=\d, sentry_client=a\/1\.0, sentry_key=abc$/),
3031
});
3132

32-
expect(new API(legacyDsn).getRequestHeaders('a', '1.0')).toMatchObject({
33+
expect(getRequestHeaders(new Dsn(legacyDsn), 'a', '1.0')).toMatchObject({
3334
'Content-Type': 'application/json',
3435
'X-Sentry-Auth': expect.stringMatching(
3536
/^Sentry sentry_version=\d, sentry_client=a\/1\.0, sentry_key=abc, sentry_secret=123$/,

0 commit comments

Comments
 (0)