Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
feat: Delete store endpoint code
  • Loading branch information
AbhiPrasad committed Apr 26, 2022
commit c9fa57b79195e3fcaa397b6faefab5a3b110703e
47 changes: 3 additions & 44 deletions packages/core/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ function getBaseApiEndpoint(dsn: DsnComponents): string {
}

/** Returns the ingest API endpoint for target. */
function _getIngestEndpoint(dsn: DsnComponents, target: 'store' | 'envelope'): string {
return `${getBaseApiEndpoint(dsn)}${dsn.projectId}/${target}/`;
function _getIngestEndpoint(dsn: DsnComponents): string {
return `${getBaseApiEndpoint(dsn)}${dsn.projectId}/envelope/`;
}

/** Returns a URL-encoded string with auth config suitable for a query string. */
Expand All @@ -49,54 +49,13 @@ function _encodedAuth(dsn: DsnComponents): string {
});
}

/** Returns the store endpoint URL. */
export function getStoreEndpoint(dsn: DsnComponents): string {
return _getIngestEndpoint(dsn, 'store');
}

/**
* Returns the store endpoint URL with auth in the query string.
*
* Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests.
*/
export function getStoreEndpointWithUrlEncodedAuth(dsn: DsnComponents): string {
return `${getStoreEndpoint(dsn)}?${_encodedAuth(dsn)}`;
}

/** Returns the envelope endpoint URL. */
function _getEnvelopeEndpoint(dsn: DsnComponents): string {
return _getIngestEndpoint(dsn, 'envelope');
}

/**
* Returns the envelope endpoint URL with auth in the query string.
*
* Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests.
*/
export function getEnvelopeEndpointWithUrlEncodedAuth(dsn: DsnComponents, tunnel?: string): string {
return tunnel ? tunnel : `${_getEnvelopeEndpoint(dsn)}?${_encodedAuth(dsn)}`;
}

/**
* Returns an object that can be used in request headers.
* This is needed for node and the old /store endpoint in sentry
*/
export function getRequestHeaders(
dsn: DsnComponents,
clientName: string,
clientVersion: string,
): { [key: string]: string } {
// CHANGE THIS to use metadata but keep clientName and clientVersion compatible
const header = [`Sentry sentry_version=${SENTRY_API_VERSION}`];
header.push(`sentry_client=${clientName}/${clientVersion}`);
header.push(`sentry_key=${dsn.publicKey}`);
if (dsn.pass) {
header.push(`sentry_secret=${dsn.pass}`);
}
return {
'Content-Type': 'application/json',
'X-Sentry-Auth': header.join(', '),
};
return tunnel ? tunnel : `${_getIngestEndpoint(dsn)}?${_encodedAuth(dsn)}`;
}

/** Returns the url to the report dialog endpoint. */
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/baseclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
*/
public sendSession(session: Session | SessionAggregates): void {
if (this._dsn) {
const [env] = createSessionEnvelope(session, this._dsn, this._options._metadata, this._options.tunnel);
const env = createSessionEnvelope(session, this._dsn, this._options._metadata, this._options.tunnel);
this.sendEnvelope(env);
}
}
Expand Down
9 changes: 1 addition & 8 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,8 @@ export {
Scope,
Session,
} from '@sentry/hub';
export {
getEnvelopeEndpointWithUrlEncodedAuth,
getStoreEndpointWithUrlEncodedAuth,
getRequestHeaders,
initAPIDetails,
getReportDialogEndpoint,
} from './api';
export { getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, getReportDialogEndpoint } from './api';
export { BaseClient } from './baseclient';
export { eventToSentryRequest, sessionToSentryRequest } from './request';
export { initAndBind } from './sdk';
export { createTransport } from './transports/base';
export { SDK_VERSION } from './version';
Expand Down
121 changes: 4 additions & 117 deletions packages/core/src/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@ import {
EventItem,
SdkInfo,
SdkMetadata,
SentryRequest,
SentryRequestType,
Session,
SessionAggregates,
SessionEnvelope,
SessionItem,
} from '@sentry/types';
import { createEnvelope, dsnToString, normalize, serializeEnvelope } from '@sentry/utils';

import { APIDetails, getEnvelopeEndpointWithUrlEncodedAuth, getStoreEndpointWithUrlEncodedAuth } from './api';
import { createEnvelope, dsnToString } from '@sentry/utils';

/** Extract sdk info from from the API metadata */
function getSdkMetadataForEnvelopeHeader(metadata?: SdkMetadata): SdkInfo | undefined {
Expand Down Expand Up @@ -47,7 +44,7 @@ export function createSessionEnvelope(
dsn: DsnComponents,
metadata?: SdkMetadata,
tunnel?: string,
): [SessionEnvelope, SentryRequestType] {
): SessionEnvelope {
const sdkInfo = getSdkMetadataForEnvelopeHeader(metadata);
const envelopeHeaders = {
sent_at: new Date().toISOString(),
Expand All @@ -62,22 +59,11 @@ export function createSessionEnvelope(
const envelopeItem = [{ type } as { type: 'session' | 'sessions' }, session] as SessionItem;
const envelope = createEnvelope<SessionEnvelope>(envelopeHeaders, [envelopeItem]);

return [envelope, type];
}

/** Creates a SentryRequest from a Session. */
export function sessionToSentryRequest(session: Session | SessionAggregates, api: APIDetails): SentryRequest {
const [envelope, type] = createSessionEnvelope(session, api.dsn, api.metadata, api.tunnel);
return {
body: serializeEnvelope(envelope),
type,
url: getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel),
};
return envelope;
}

/**
* Create an Envelope from an event. Note that this is duplicated from below,
* but on purpose as this will be refactored in v7.
* Create an Envelope from an event.
*/
export function createEventEnvelope(
event: Event,
Expand Down Expand Up @@ -135,102 +121,3 @@ export function createEventEnvelope(
];
return createEnvelope<EventEnvelope>(envelopeHeaders, [eventItem]);
}

/** Creates a SentryRequest from an event. */
export function eventToSentryRequest(event: Event, api: APIDetails): SentryRequest {
const sdkInfo = getSdkMetadataForEnvelopeHeader(api.metadata);
const eventType = event.type || 'event';
const useEnvelope = eventType === 'transaction' || !!api.tunnel;

const { transactionSampling } = event.sdkProcessingMetadata || {};
const { method: samplingMethod, rate: sampleRate } = transactionSampling || {};

// TODO: Below is a temporary hack in order to debug a serialization error - see
// https://github.com/getsentry/sentry-javascript/issues/2809,
// https://github.com/getsentry/sentry-javascript/pull/4425, and
// https://github.com/getsentry/sentry-javascript/pull/4574.
//
// TL; DR: even though we normalize all events (which should prevent this), something is causing `JSON.stringify` to
// throw a circular reference error.
//
// When it's time to remove it:
// 1. Delete everything between here and where the request object `req` is created, EXCEPT the line deleting
// `sdkProcessingMetadata`
// 2. Restore the original version of the request body, which is commented out
// 3. Search for either of the PR URLs above and pull out the companion hacks in the browser playwright tests and the
// baseClient tests in this package
enhanceEventWithSdkInfo(event, api.metadata.sdk);
event.tags = event.tags || {};
event.extra = event.extra || {};

// In theory, all events should be marked as having gone through normalization and so
// we should never set this tag/extra data
if (!(event.sdkProcessingMetadata && event.sdkProcessingMetadata.baseClientNormalized)) {
event.tags.skippedNormalization = true;
event.extra.normalizeDepth = event.sdkProcessingMetadata ? event.sdkProcessingMetadata.normalizeDepth : 'unset';
}

// prevent this data from being sent to sentry
// TODO: This is NOT part of the hack - DO NOT DELETE
delete event.sdkProcessingMetadata;

let body;
try {
// 99.9% of events should get through just fine - no change in behavior for them
body = JSON.stringify(event);
} catch (err) {
// Record data about the error without replacing original event data, then force renormalization
event.tags.JSONStringifyError = true;
event.extra.JSONStringifyError = err;
try {
body = JSON.stringify(normalize(event));
} catch (newErr) {
// At this point even renormalization hasn't worked, meaning something about the event data has gone very wrong.
// Time to cut our losses and record only the new error. With luck, even in the problematic cases we're trying to
// debug with this hack, we won't ever land here.
const innerErr = newErr as Error;
body = JSON.stringify({
message: 'JSON.stringify error after renormalization',
// setting `extra: { innerErr }` here for some reason results in an empty object, so unpack manually
extra: { message: innerErr.message, stack: innerErr.stack },
});
}
}

const req: SentryRequest = {
// this is the relevant line of code before the hack was added, to make it easy to undo said hack once we've solved
// the mystery
// body: JSON.stringify(sdkInfo ? enhanceEventWithSdkInfo(event, api.metadata.sdk) : event),
body,
type: eventType,
url: useEnvelope
? getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel)
: getStoreEndpointWithUrlEncodedAuth(api.dsn),
};

// https://develop.sentry.dev/sdk/envelopes/

// Since we don't need to manipulate envelopes nor store them, there is no
// exported concept of an Envelope with operations including serialization and
// deserialization. Instead, we only implement a minimal subset of the spec to
// serialize events inline here.
if (useEnvelope) {
const envelopeHeaders = {
event_id: event.event_id as string,
sent_at: new Date().toISOString(),
...(sdkInfo && { sdk: sdkInfo }),
...(!!api.tunnel && { dsn: dsnToString(api.dsn) }),
};
const eventItem: EventItem = [
{
type: eventType,
sample_rates: [{ id: samplingMethod, rate: sampleRate }],
},
req.body,
];
const envelope = createEnvelope<EventEnvelope>(envelopeHeaders, [eventItem]);
req.body = serializeEnvelope(envelope);
}

return req;
}
31 changes: 1 addition & 30 deletions packages/core/test/lib/api.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
/* eslint-disable deprecation/deprecation */
import { makeDsn } from '@sentry/utils';

import {
getEnvelopeEndpointWithUrlEncodedAuth,
getReportDialogEndpoint,
getRequestHeaders,
getStoreEndpoint,
getStoreEndpointWithUrlEncodedAuth,
initAPIDetails,
} from '../../src/api';
import { getEnvelopeEndpointWithUrlEncodedAuth, getReportDialogEndpoint, initAPIDetails } from '../../src/api';

const ingestDsn = 'https://[email protected]:1234/subpath/123';
const dsnPublic = 'https://[email protected]:1234/subpath/123';
Expand All @@ -19,14 +12,6 @@ const ingestDsnAPI = initAPIDetails(ingestDsn);
const dsnPublicAPI = initAPIDetails(dsnPublic);

describe('API', () => {
test('getStoreEndpoint', () => {
expect(getStoreEndpointWithUrlEncodedAuth(dsnPublicAPI.dsn)).toEqual(
'https://sentry.io:1234/subpath/api/123/store/?sentry_key=abc&sentry_version=7',
);
expect(getStoreEndpoint(dsnPublicAPI.dsn)).toEqual('https://sentry.io:1234/subpath/api/123/store/');
expect(getStoreEndpoint(ingestDsnAPI.dsn)).toEqual('https://xxxx.ingest.sentry.io:1234/subpath/api/123/store/');
});

test('getEnvelopeEndpoint', () => {
expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicAPI.dsn)).toEqual(
'https://sentry.io:1234/subpath/api/123/envelope/?sentry_key=abc&sentry_version=7',
Expand All @@ -35,20 +20,6 @@ describe('API', () => {
expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicAPIWithTunnel.dsn, tunnel)).toEqual(tunnel);
});

test('getRequestHeaders', () => {
expect(getRequestHeaders(makeDsn(dsnPublic), 'a', '1.0')).toMatchObject({
'Content-Type': 'application/json',
'X-Sentry-Auth': expect.stringMatching(/^Sentry sentry_version=\d, sentry_client=a\/1\.0, sentry_key=abc$/),
});

expect(getRequestHeaders(makeDsn(legacyDsn), 'a', '1.0')).toMatchObject({
'Content-Type': 'application/json',
'X-Sentry-Auth': expect.stringMatching(
/^Sentry sentry_version=\d, sentry_client=a\/1\.0, sentry_key=abc, sentry_secret=123$/,
),
});
});

describe('getReportDialogEndpoint', () => {
test.each([
[
Expand Down
Loading