Skip to content

Commit

Permalink
refactor post-payload
Browse files Browse the repository at this point in the history
  • Loading branch information
gaojude committed Dec 12, 2024
1 parent 31baaf6 commit 54144f3
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 25 deletions.
89 changes: 89 additions & 0 deletions packages/next/src/telemetry/post-telemetry-payload.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { postNextTelemetryPayload } from './post-telemetry-payload'

describe('postNextTelemetryPayload', () => {
let originalFetch: typeof fetch

beforeEach(() => {
originalFetch = global.fetch
})

afterEach(() => {
global.fetch = originalFetch
})

it('sends telemetry payload successfully', async () => {
const mockFetch = jest.fn().mockResolvedValue({
ok: true,
})
global.fetch = mockFetch

const payload = {
meta: { version: '1.0' },
context: {
anonymousId: 'test-id',
projectId: 'test-project',
sessionId: 'test-session',
},
events: [
{
eventName: 'test-event',
fields: { foo: 'bar' },
},
],
}

await postNextTelemetryPayload(payload)

expect(mockFetch).toHaveBeenCalledWith(
'https://telemetry.nextjs.org/api/v1/record',
{
method: 'POST',
body: JSON.stringify(payload),
headers: { 'content-type': 'application/json' },
signal: expect.any(AbortSignal),
}
)
})

it('retries on failure', async () => {
const mockFetch = jest
.fn()
.mockRejectedValueOnce(new Error('Network error'))
.mockResolvedValueOnce({ ok: true })
global.fetch = mockFetch

const payload = {
meta: {},
context: {
anonymousId: 'test-id',
projectId: 'test-project',
sessionId: 'test-session',
},
events: [],
}

await postNextTelemetryPayload(payload)

expect(mockFetch).toHaveBeenCalledTimes(2)
})

it('swallows errors after retries exhausted', async () => {
const mockFetch = jest.fn().mockRejectedValue(new Error('Network error'))
global.fetch = mockFetch

const payload = {
meta: {},
context: {
anonymousId: 'test-id',
projectId: 'test-project',
sessionId: 'test-session',
},
events: [],
}

// Should not throw
await postNextTelemetryPayload(payload)

expect(mockFetch).toHaveBeenCalledTimes(2) // Initial try + 1 retry
})
})
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
import retry from 'next/dist/compiled/async-retry'

export function _postPayload(endpoint: string, body: object, signal?: any) {
interface Payload {
meta: { [key: string]: unknown }

context: {
anonymousId: string
projectId: string
sessionId: string
}

events: Array<{
eventName: string
fields: object
}>
}

export function postNextTelemetryPayload(payload: Payload, signal?: any) {
if (!signal && 'timeout' in AbortSignal) {
signal = AbortSignal.timeout(5000)
}
return (
retry(
() =>
fetch(endpoint, {
fetch('https://telemetry.nextjs.org/api/v1/record', {
method: 'POST',
body: JSON.stringify(body),
body: JSON.stringify(payload),
headers: { 'content-type': 'application/json' },
signal,
}).then((res) => {
Expand Down
31 changes: 9 additions & 22 deletions packages/next/src/telemetry/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import path from 'path'

import { getAnonymousMeta } from './anonymous-meta'
import * as ciEnvironment from '../server/ci-info'
import { _postPayload } from './post-payload'
import { postNextTelemetryPayload } from './post-telemetry-payload'
import { getRawProjectId } from './project-id'
import { AbortController } from 'next/dist/compiled/@edge-runtime/ponyfill'
import fs from 'fs'
Expand All @@ -30,16 +30,6 @@ const TELEMETRY_KEY_ID = `telemetry.anonymousId`
const TELEMETRY_KEY_SALT = `telemetry.salt`

export type TelemetryEvent = { eventName: string; payload: object }
type EventContext = {
anonymousId: string
projectId: string
sessionId: string
}
type EventMeta = { [key: string]: unknown }
type EventBatchShape = {
eventName: string
fields: object
}

type RecordObject = {
isFulfilled: boolean
Expand Down Expand Up @@ -302,22 +292,19 @@ export class Telemetry {
return Promise.resolve()
}

const context: EventContext = {
anonymousId: this.anonymousId,
projectId: await this.getProjectId(),
sessionId: this.sessionId,
}
const meta: EventMeta = getAnonymousMeta()
const postController = new AbortController()
const res = _postPayload(
`https://telemetry.nextjs.org/api/v1/record`,
const res = postNextTelemetryPayload(
{
context,
meta,
context: {
anonymousId: this.anonymousId,
projectId: await this.getProjectId(),
sessionId: this.sessionId,
},
meta: getAnonymousMeta(),
events: events.map(({ eventName, payload }) => ({
eventName,
fields: payload,
})) as Array<EventBatchShape>,
})),
},
postController.signal
)
Expand Down

0 comments on commit 54144f3

Please sign in to comment.