From 44cc464eea0c2ca9ea66bd9221a5ba86c1fcd86e Mon Sep 17 00:00:00 2001 From: Neek Sandhu Date: Tue, 12 Jul 2022 12:32:39 -0700 Subject: [PATCH] Add support for segment inspector (#413) This patch adds event reporting to Segment Inspector. The reporting happens through the interface injected by the extension itself. --- .changeset/khaki-frogs-look.md | 5 ++ packages/browser/package.json | 3 +- packages/browser/src/core/analytics/index.ts | 15 +++++ .../core/inspector/__tests__/index.test.ts | 59 +++++++++++++++++++ packages/browser/src/core/inspector/index.ts | 15 +++++ .../browser/src/core/queue/event-queue.ts | 17 ++++++ yarn.lock | 8 +++ 7 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 .changeset/khaki-frogs-look.md create mode 100644 packages/browser/src/core/inspector/__tests__/index.test.ts create mode 100644 packages/browser/src/core/inspector/index.ts diff --git a/.changeset/khaki-frogs-look.md b/.changeset/khaki-frogs-look.md new file mode 100644 index 000000000..d0d8b2d84 --- /dev/null +++ b/.changeset/khaki-frogs-look.md @@ -0,0 +1,5 @@ +--- +'@segment/analytics-next': minor +--- + +Add support for Segment Inspector Chrome extension diff --git a/packages/browser/package.json b/packages/browser/package.json index d38d19dc4..5a5a6e02b 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -43,7 +43,7 @@ "size-limit": [ { "path": "dist/umd/index.js", - "limit": "25.6 KB" + "limit": "25.8 KB" } ], "dependencies": { @@ -59,6 +59,7 @@ "unfetch": "^4.1.0" }, "devDependencies": { + "@segment/inspector-core": "^1.0.0", "@size-limit/preset-big-lib": "^7.0.8", "@types/flat": "^5.0.1", "@types/fs-extra": "^9.0.2", diff --git a/packages/browser/src/core/analytics/index.ts b/packages/browser/src/core/analytics/index.ts index 0cb6c43cf..387e7af05 100644 --- a/packages/browser/src/core/analytics/index.ts +++ b/packages/browser/src/core/analytics/index.ts @@ -29,6 +29,7 @@ import type { import { version } from '../../generated/version' import { PriorityQueue } from '../../lib/priority-queue' import { getGlobal } from '../../lib/get-global' +import { inspectorHost } from '../inspector' const deprecationWarning = 'This is being deprecated and will be not be available in future releases of Analytics JS' @@ -114,6 +115,13 @@ export class Analytics extends Emitter { this.integrations = options?.integrations ?? {} this.options = options ?? {} + inspectorHost.start({ + user: { + id: this.user().id() || null, + traits: this.user().traits(), + }, + }) + autoBind(this) } @@ -321,6 +329,13 @@ export class Analytics extends Emitter { ): Promise { const ctx = new Context(event) + inspectorHost.trace({ + stage: 'triggered', + id: ctx.id, + event: event as any, + timestamp: new Date().toISOString(), + }) + if (isOffline() && !this.options.retryQueue) { return ctx } diff --git a/packages/browser/src/core/inspector/__tests__/index.test.ts b/packages/browser/src/core/inspector/__tests__/index.test.ts new file mode 100644 index 000000000..0eba5e997 --- /dev/null +++ b/packages/browser/src/core/inspector/__tests__/index.test.ts @@ -0,0 +1,59 @@ +import { Analytics } from '../../analytics' + +let analytics: Analytics + +describe('Inspector interface', () => { + beforeEach(() => { + window.__SEGMENT_INSPECTOR__ = { + start: jest.fn(), + trace: jest.fn(), + } + + analytics = new Analytics({ + writeKey: 'abc', + }) + }) + + it('accepts and starts up an inspector client trying to connect', () => { + expect(window.__SEGMENT_INSPECTOR__?.start).toHaveBeenCalledTimes(1) + }) + + it('notifies the connected inspector client about each event API call and delivery', async () => { + expect(window.__SEGMENT_INSPECTOR__?.trace).not.toHaveBeenCalled() + + const timestampMatcher = expect.stringMatching( + /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/ + ) + const deliveryPromise = analytics.track('Test event').catch(() => {}) + + // expect 2 calls, triggered report, followed enriched report + expect(window.__SEGMENT_INSPECTOR__?.trace).toHaveBeenCalledTimes(2) + + expect(window.__SEGMENT_INSPECTOR__?.trace).toHaveBeenCalledWith( + expect.objectContaining({ + stage: 'triggered', + timestamp: timestampMatcher, + event: expect.objectContaining({ + event: 'Test event', + type: 'track', + }), + }) + ) + + await deliveryPromise + + // triggered -> enriched -> delivered + expect(window.__SEGMENT_INSPECTOR__?.trace).toHaveBeenCalledTimes(3) + + expect(window.__SEGMENT_INSPECTOR__?.trace).toHaveBeenCalledWith( + expect.objectContaining({ + stage: 'delivered', + timestamp: timestampMatcher, + event: expect.objectContaining({ + event: 'Test event', + type: 'track', + }), + }) + ) + }) +}) diff --git a/packages/browser/src/core/inspector/index.ts b/packages/browser/src/core/inspector/index.ts new file mode 100644 index 000000000..f4f434b0d --- /dev/null +++ b/packages/browser/src/core/inspector/index.ts @@ -0,0 +1,15 @@ +import type { Inspector } from '@segment/inspector-core' +import { getGlobal } from '../../lib/get-global' + +declare global { + interface Window { + __SEGMENT_INSPECTOR__?: Inspector + } +} + +const env = getGlobal() + +export const inspectorHost: Inspector = { + start: (...args) => (env as any)?.['__SEGMENT_INSPECTOR__']?.start(...args), + trace: (...args) => (env as any)?.['__SEGMENT_INSPECTOR__']?.trace(...args), +} diff --git a/packages/browser/src/core/queue/event-queue.ts b/packages/browser/src/core/queue/event-queue.ts index be4b1c777..5f2102304 100644 --- a/packages/browser/src/core/queue/event-queue.ts +++ b/packages/browser/src/core/queue/event-queue.ts @@ -8,6 +8,7 @@ import { Emitter } from '../emitter' import { Integrations } from '../events' import { Plugin } from '../plugin' import { attempt, ensure } from './delivery' +import { inspectorHost } from '../inspector' type PluginsByType = { before: Plugin[] @@ -265,6 +266,13 @@ export class EventQueue extends Emitter { } } + inspectorHost.trace({ + stage: 'enriched', + id: ctx.id, + event: ctx.event as any, + timestamp: new Date().toISOString(), + }) + // Enrichment and before plugins can re-arrange the deny list dynamically // so we need to pluck them at the end const { destinations, after } = this.availableExtensions( @@ -282,6 +290,15 @@ export class EventQueue extends Emitter { ctx.stats.increment('message_delivered') + inspectorHost.trace({ + stage: 'delivered', + id: ctx.id, + event: ctx.event as any, + timestamp: new Date().toISOString(), + // FIXME: Resolve browsers destinations that the event was sent to + destinations: ['segment.io'], + }) + const afterCalls = after.map((after) => attempt(ctx, after)) await Promise.all(afterCalls) diff --git a/yarn.lock b/yarn.lock index fe611e004..905e2816e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1491,6 +1491,7 @@ __metadata: "@lukeed/uuid": ^2.0.0 "@segment/analytics.js-video-plugins": ^0.2.1 "@segment/facade": ^3.4.9 + "@segment/inspector-core": ^1.0.0 "@segment/tsub": ^0.1.12 "@size-limit/preset-big-lib": ^7.0.8 "@types/flat": ^5.0.1 @@ -1570,6 +1571,13 @@ __metadata: languageName: node linkType: hard +"@segment/inspector-core@npm:^1.0.0": + version: 1.0.0 + resolution: "@segment/inspector-core@npm:1.0.0" + checksum: 4c04303301f54cab9fe8d0502fe0a755ad21206b8917ebbfb5edb8be5888cd7ed4dc948cf34e52b957863de44f9152551d78571ba12c00106f573fdb2136f4ef + languageName: node + linkType: hard + "@segment/isodate-traverse@npm:^1.1.1": version: 1.1.1 resolution: "@segment/isodate-traverse@npm:1.1.1"