Skip to content

Commit

Permalink
feat(node): Add checkin envelope types (#7777)
Browse files Browse the repository at this point in the history
Co-authored-by: Lukas Stracke <lukas.stracke@sentry.io>
  • Loading branch information
AbhiPrasad and Lms24 committed Apr 7, 2023
1 parent ad8ce23 commit a2cda4d
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 4 deletions.
33 changes: 33 additions & 0 deletions packages/node/src/checkin.ts
@@ -0,0 +1,33 @@
import type { CheckIn, CheckInEvelope, CheckInItem, DsnComponents, SdkMetadata } from '@sentry/types';
import { createEnvelope, dsnToString } from '@sentry/utils';

/**
* Create envelope from check in item.
*/
export function createCheckInEnvelope(
checkIn: CheckIn,
metadata?: SdkMetadata,
tunnel?: string,
dsn?: DsnComponents,
): CheckInEvelope {
const headers: CheckInEvelope[0] = {
sent_at: new Date().toISOString(),
...(metadata &&
metadata.sdk && {
sdk: {
name: metadata.sdk.name,
version: metadata.sdk.version,
},
}),
...(!!tunnel && !!dsn && { dsn: dsnToString(dsn) }),
};
const item = createCheckInEnvelopeItem(checkIn);
return createEnvelope<CheckInEvelope>(headers, [item]);
}

function createCheckInEnvelopeItem(checkIn: CheckIn): CheckInItem {
const checkInHeaders: CheckInItem[0] = {
type: 'check_in',
};
return [checkInHeaders, checkIn];
}
61 changes: 61 additions & 0 deletions packages/node/test/checkin.test.ts
@@ -0,0 +1,61 @@
import { createCheckInEnvelope } from '../src/checkin';

describe('userFeedback', () => {
test('creates user feedback envelope header', () => {
const envelope = createCheckInEnvelope(
{
check_in_id: '83a7c03ed0a04e1b97e2e3b18d38f244',
monitor_slug: 'b7645b8e-b47d-4398-be9a-d16b0dac31cb',
status: 'in_progress',
},
{
sdk: {
name: 'testSdkName',
version: 'testSdkVersion',
},
},
'testTunnel',
{
host: 'testHost',
projectId: 'testProjectId',
protocol: 'http',
},
);

expect(envelope[0]).toEqual({
dsn: 'http://undefined@testHost/undefinedtestProjectId',
sdk: {
name: 'testSdkName',
version: 'testSdkVersion',
},
sent_at: expect.any(String),
});
});

test('creates user feedback envelope item', () => {
const envelope = createCheckInEnvelope({
check_in_id: '83a7c03ed0a04e1b97e2e3b18d38f244',
monitor_slug: 'b7645b8e-b47d-4398-be9a-d16b0dac31cb',
status: 'ok',
duration: 10.0,
release: '1.0.0',
environment: 'production',
});

expect(envelope[1]).toEqual([
[
{
type: 'check_in',
},
{
check_in_id: '83a7c03ed0a04e1b97e2e3b18d38f244',
monitor_slug: 'b7645b8e-b47d-4398-be9a-d16b0dac31cb',
status: 'ok',
duration: 10.0,
release: '1.0.0',
environment: 'production',
},
],
]);
});
});
13 changes: 13 additions & 0 deletions packages/types/src/checkin.ts
@@ -0,0 +1,13 @@
// https://develop.sentry.dev/sdk/check-ins/
export interface CheckIn {
// Check-In ID (unique and client generated).
check_in_id: string;
// The distinct slug of the monitor.
monitor_slug: string;
// The status of the check-in.
status: 'in_progress' | 'ok' | 'error';
// The duration of the check-in in seconds. Will only take effect if the status is ok or error.
duration?: number;
release?: string;
environment?: string;
}
6 changes: 4 additions & 2 deletions packages/types/src/datacategory.ts
@@ -1,7 +1,7 @@
// This type is used in various places like Client Reports and Rate Limit Categories
// See:
// - https://develop.sentry.dev/sdk/rate-limiting/#definitions
// - https://github.com/getsentry/relay/blob/10874b587bb676bd6d50ad42d507216513660082/relay-common/src/constants.rs#L97-L113
// - https://github.com/getsentry/relay/blob/c3b339e151c1e548ede489a01c65db82472c8751/relay-common/src/constants.rs#L139-L152
// - https://develop.sentry.dev/sdk/client-reports/#envelope-item-payload under `discarded_events`
export type DataCategory =
// Reserved and only used in edgecases, unlikely to be ever actually used
Expand All @@ -21,4 +21,6 @@ export type DataCategory =
// SDK internal event, like client_reports
| 'internal'
// Profile event type
| 'profile';
| 'profile'
// Check-in event (monitor)
| 'monitor';
10 changes: 8 additions & 2 deletions packages/types/src/envelope.ts
@@ -1,3 +1,4 @@
import type { CheckIn } from './checkin';
import type { ClientReport } from './clientreport';
import type { DsnComponents } from './dsn';
import type { Event } from './event';
Expand Down Expand Up @@ -31,7 +32,8 @@ export type EnvelopeItemType =
| 'event'
| 'profile'
| 'replay_event'
| 'replay_recording';
| 'replay_recording'
| 'check_in';

export type BaseEnvelopeHeaders = {
[key: string]: unknown;
Expand Down Expand Up @@ -68,6 +70,7 @@ type SessionAggregatesItemHeaders = { type: 'sessions' };
type ClientReportItemHeaders = { type: 'client_report' };
type ReplayEventItemHeaders = { type: 'replay_event' };
type ReplayRecordingItemHeaders = { type: 'replay_recording'; length: number };
type CheckInItemHeaders = { type: 'check_in' };

export type EventItem = BaseEnvelopeItem<EventItemHeaders, Event>;
export type AttachmentItem = BaseEnvelopeItem<AttachmentItemHeaders, string | Uint8Array>;
Expand All @@ -76,18 +79,21 @@ export type SessionItem =
| BaseEnvelopeItem<SessionItemHeaders, Session>
| BaseEnvelopeItem<SessionAggregatesItemHeaders, SessionAggregates>;
export type ClientReportItem = BaseEnvelopeItem<ClientReportItemHeaders, ClientReport>;
export type CheckInItem = BaseEnvelopeItem<CheckInItemHeaders, CheckIn>;
type ReplayEventItem = BaseEnvelopeItem<ReplayEventItemHeaders, ReplayEvent>;
type ReplayRecordingItem = BaseEnvelopeItem<ReplayRecordingItemHeaders, ReplayRecordingData>;

export type EventEnvelopeHeaders = { event_id: string; sent_at: string; trace?: DynamicSamplingContext };
type SessionEnvelopeHeaders = { sent_at: string };
type CheckInEnvelopeHeaders = BaseEnvelopeHeaders;
type ClientReportEnvelopeHeaders = BaseEnvelopeHeaders;
type ReplayEnvelopeHeaders = BaseEnvelopeHeaders;

export type EventEnvelope = BaseEnvelope<EventEnvelopeHeaders, EventItem | AttachmentItem | UserFeedbackItem>;
export type SessionEnvelope = BaseEnvelope<SessionEnvelopeHeaders, SessionItem>;
export type ClientReportEnvelope = BaseEnvelope<ClientReportEnvelopeHeaders, ClientReportItem>;
export type ReplayEnvelope = [ReplayEnvelopeHeaders, [ReplayEventItem, ReplayRecordingItem]];
export type CheckInEvelope = BaseEnvelope<CheckInEnvelopeHeaders, CheckInItem>;

export type Envelope = EventEnvelope | SessionEnvelope | ClientReportEnvelope | ReplayEnvelope;
export type Envelope = EventEnvelope | SessionEnvelope | ClientReportEnvelope | ReplayEnvelope | CheckInEvelope;
export type EnvelopeItem = Envelope[1][number];
3 changes: 3 additions & 0 deletions packages/types/src/index.ts
Expand Up @@ -30,6 +30,8 @@ export type {
SessionEnvelope,
SessionItem,
UserFeedbackItem,
CheckInItem,
CheckInEvelope,
} from './envelope';
export type { ExtendedError } from './error';
export type { Event, EventHint, EventType, ErrorEvent, TransactionEvent } from './event';
Expand Down Expand Up @@ -104,3 +106,4 @@ export type { Instrumenter } from './instrumenter';
export type { HandlerDataFetch, HandlerDataXhr, SentryXhrData, SentryWrappedXMLHttpRequest } from './instrument';

export type { BrowserClientReplayOptions } from './browseroptions';
export type { CheckIn } from './checkin';
1 change: 1 addition & 0 deletions packages/utils/src/envelope.ts
Expand Up @@ -207,6 +207,7 @@ const ITEM_TYPE_TO_DATA_CATEGORY_MAP: Record<EnvelopeItemType, DataCategory> = {
profile: 'profile',
replay_event: 'replay',
replay_recording: 'replay',
check_in: 'monitor',
};

/**
Expand Down

0 comments on commit a2cda4d

Please sign in to comment.