From 3b0c7b03ac5e25778beca1971db5ce2ab84614c9 Mon Sep 17 00:00:00 2001 From: Joe Ayoub <45374896+joe-ayoub-segment@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:50:04 +0000 Subject: [PATCH] Fullstory web API upgrade (#1869) * Fullstory web api upgrade * adding more files * PR feedback for FullStory Web API upgrade (#1879) * PR feedback * documentation tweaks --------- Co-authored-by: Scott Norvell --- .../destinations/fullstory/package.json | 2 +- .../fullstory/src/__tests__/fullstory.test.ts | 212 +++++++++----- .../src/__tests__/fullstoryV2.test.ts | 277 ++++++++++++++++++ .../src/__tests__/initialization.test.ts | 81 +++++ .../src/identifyUserV2/generated-types.ts | 26 ++ .../fullstory/src/identifyUserV2/index.ts | 95 ++++++ .../destinations/fullstory/src/index.ts | 27 +- .../src/trackEventV2/generated-types.ts | 14 + .../fullstory/src/trackEventV2/index.ts | 44 +++ .../destinations/fullstory/src/types.ts | 12 +- .../src/viewedPageV2/generated-types.ts | 14 + .../fullstory/src/viewedPageV2/index.ts | 52 ++++ yarn.lock | 18 +- 13 files changed, 767 insertions(+), 107 deletions(-) create mode 100644 packages/browser-destinations/destinations/fullstory/src/__tests__/fullstoryV2.test.ts create mode 100644 packages/browser-destinations/destinations/fullstory/src/__tests__/initialization.test.ts create mode 100644 packages/browser-destinations/destinations/fullstory/src/identifyUserV2/generated-types.ts create mode 100644 packages/browser-destinations/destinations/fullstory/src/identifyUserV2/index.ts create mode 100644 packages/browser-destinations/destinations/fullstory/src/trackEventV2/generated-types.ts create mode 100644 packages/browser-destinations/destinations/fullstory/src/trackEventV2/index.ts create mode 100644 packages/browser-destinations/destinations/fullstory/src/viewedPageV2/generated-types.ts create mode 100644 packages/browser-destinations/destinations/fullstory/src/viewedPageV2/index.ts diff --git a/packages/browser-destinations/destinations/fullstory/package.json b/packages/browser-destinations/destinations/fullstory/package.json index c3b266e96f..ed2addbe94 100644 --- a/packages/browser-destinations/destinations/fullstory/package.json +++ b/packages/browser-destinations/destinations/fullstory/package.json @@ -15,7 +15,7 @@ }, "typings": "./dist/esm", "dependencies": { - "@fullstory/browser": "^1.4.9", + "@fullstory/browser": "^2.0.3", "@segment/actions-core": "^3.97.0", "@segment/browser-destination-runtime": "^1.27.0" }, diff --git a/packages/browser-destinations/destinations/fullstory/src/__tests__/fullstory.test.ts b/packages/browser-destinations/destinations/fullstory/src/__tests__/fullstory.test.ts index c3cf7dff89..861614e765 100644 --- a/packages/browser-destinations/destinations/fullstory/src/__tests__/fullstory.test.ts +++ b/packages/browser-destinations/destinations/fullstory/src/__tests__/fullstory.test.ts @@ -1,6 +1,12 @@ import { Analytics, Context } from '@segment/analytics-next' -import fullstory, { destination } from '..' +import fullstory from '..' +import trackEvent from '../trackEvent' +import identifyUser from '../identifyUser' +import viewedPage from '../viewedPage' import { Subscription } from '@segment/browser-destination-runtime/types' +import { defaultValues } from '@segment/actions-core/*' + +const FakeOrgId = 'asdf-qwer' const example: Subscription[] = [ { @@ -8,82 +14,36 @@ const example: Subscription[] = [ name: 'Track Event', enabled: true, subscribe: 'type = "track"', - mapping: { - name: { - '@path': '$.name' - }, - properties: { - '@path': '$.properties' - } - } + mapping: defaultValues(trackEvent.fields) }, { partnerAction: 'identifyUser', name: 'Identify User', enabled: true, subscribe: 'type = "identify"', - mapping: { - anonymousId: { - '@path': '$.anonymousId' - }, - userId: { - '@path': '$.userId' - }, - email: { - '@path': '$.traits.email' - }, - traits: { - '@path': '$.traits' - }, - displayName: { - '@path': '$.traits.name' - } - } + mapping: defaultValues(identifyUser.fields) + }, + { + partnerAction: 'viewedPage', + name: 'Viewed Page', + enabled: true, + subscribe: 'type = "page"', + mapping: defaultValues(viewedPage.fields) } ] -test('can load fullstory', async () => { - const [event] = await fullstory({ - orgId: 'thefullstory.com', - subscriptions: example - }) - - jest.spyOn(destination.actions.trackEvent, 'perform') - jest.spyOn(destination, 'initialize') - - await event.load(Context.system(), {} as Analytics) - expect(destination.initialize).toHaveBeenCalled() - - const ctx = await event.track?.( - new Context({ - type: 'track', - properties: { - banana: '📞' - } - }) - ) - - expect(destination.actions.trackEvent.perform).toHaveBeenCalled() - expect(ctx).not.toBeUndefined() - - const scripts = window.document.querySelectorAll('script') - expect(scripts).toMatchInlineSnapshot(` - NodeList [ - , - ] - `) +beforeEach(() => { + delete window._fs_initialized + if (window._fs_namespace) { + delete window[window._fs_namespace] + delete window._fs_namespace + } }) describe('#track', () => { it('sends record events to fullstory on "event"', async () => { const [event] = await fullstory({ - orgId: 'thefullstory.com', + orgId: FakeOrgId, subscriptions: example }) @@ -93,7 +53,7 @@ describe('#track', () => { await event.track?.( new Context({ type: 'track', - name: 'hello!', + event: 'hello!', properties: { banana: '📞' } @@ -112,16 +72,16 @@ describe('#track', () => { describe('#identify', () => { it('should default to anonymousId', async () => { - const [_, identifyUser] = await fullstory({ - orgId: 'thefullstory.com', + const [_, identify] = await fullstory({ + orgId: FakeOrgId, subscriptions: example }) - await identifyUser.load(Context.system(), {} as Analytics) + await identify.load(Context.system(), {} as Analytics) const fs = jest.spyOn(window.FS, 'setUserVars') const fsId = jest.spyOn(window.FS, 'identify') - await identifyUser.identify?.( + await identify.identify?.( new Context({ type: 'identify', anonymousId: 'anon', @@ -137,7 +97,7 @@ describe('#identify', () => { }), it('should send an id', async () => { const [_, identifyUser] = await fullstory({ - orgId: 'thefullstory.com', + orgId: FakeOrgId, subscriptions: example }) await identifyUser.load(Context.system(), {} as Analytics) @@ -147,14 +107,14 @@ describe('#identify', () => { expect(fsId).toHaveBeenCalledWith('id', {}, 'segment-browser-actions') }), it('should camelCase custom traits', async () => { - const [_, identifyUser] = await fullstory({ - orgId: 'thefullstory.com', + const [_, identify] = await fullstory({ + orgId: FakeOrgId, subscriptions: example }) - await identifyUser.load(Context.system(), {} as Analytics) + await identify.load(Context.system(), {} as Analytics) const fsId = jest.spyOn(window.FS, 'identify') - await identifyUser.identify?.( + await identify.identify?.( new Context({ type: 'identify', userId: 'id', @@ -173,15 +133,15 @@ describe('#identify', () => { }) it('can set user vars', async () => { - const [_, identifyUser] = await fullstory({ - orgId: 'thefullstory.com', + const [_, identify] = await fullstory({ + orgId: FakeOrgId, subscriptions: example }) - await identifyUser.load(Context.system(), {} as Analytics) + await identify.load(Context.system(), {} as Analytics) const fs = jest.spyOn(window.FS, 'setUserVars') - await identifyUser.identify?.( + await identify.identify?.( new Context({ type: 'identify', traits: { @@ -204,15 +164,15 @@ describe('#identify', () => { }) it('should set displayName correctly', async () => { - const [_, identifyUser] = await fullstory({ - orgId: 'thefullstory.com', + const [_, identify] = await fullstory({ + orgId: FakeOrgId, subscriptions: example }) - await identifyUser.load(Context.system(), {} as Analytics) + await identify.load(Context.system(), {} as Analytics) const fs = jest.spyOn(window.FS, 'identify') - await identifyUser.identify?.( + await identify.identify?.( new Context({ type: 'identify', userId: 'userId', @@ -236,3 +196,93 @@ describe('#identify', () => { ) }) }) + +describe('#page', () => { + it('sends page events to fullstory on "page" (category edition)', async () => { + const [, , viewed] = await fullstory({ + orgId: FakeOrgId, + subscriptions: example + }) + + await viewed.load(Context.system(), {} as Analytics) + const fs = jest.spyOn(window.FS, 'setVars') + + await viewed.page?.( + new Context({ + type: 'page', + category: 'Walruses', + name: 'Walrus Page', + properties: { + banana: '📞' + } + }) + ) + + expect(fs).toHaveBeenCalledWith( + 'page', + { + pageName: 'Walruses', + banana: '📞' + }, + 'segment-browser-actions' + ) + }) + + it('sends page events to fullstory on "page" (name edition)', async () => { + const [, , viewed] = await fullstory({ + orgId: FakeOrgId, + subscriptions: example + }) + + await viewed.load(Context.system(), {} as Analytics) + const fs = jest.spyOn(window.FS, 'setVars') + + await viewed.page?.( + new Context({ + type: 'page', + name: 'Walrus Page', + properties: { + banana: '📞' + } + }) + ) + + expect(fs).toHaveBeenCalledWith( + 'page', + { + pageName: 'Walrus Page', + banana: '📞' + }, + 'segment-browser-actions' + ) + }) + + it('sends page events to fullstory on "page" (no pageName edition)', async () => { + const [, , viewed] = await fullstory({ + orgId: FakeOrgId, + subscriptions: example + }) + + await viewed.load(Context.system(), {} as Analytics) + const fs = jest.spyOn(window.FS, 'setVars') + + await viewed.page?.( + new Context({ + type: 'page', + properties: { + banana: '📞', + keys: '🗝🔑' + } + }) + ) + + expect(fs).toHaveBeenCalledWith( + 'page', + { + banana: '📞', + keys: '🗝🔑' + }, + 'segment-browser-actions' + ) + }) +}) diff --git a/packages/browser-destinations/destinations/fullstory/src/__tests__/fullstoryV2.test.ts b/packages/browser-destinations/destinations/fullstory/src/__tests__/fullstoryV2.test.ts new file mode 100644 index 0000000000..2399ce96c4 --- /dev/null +++ b/packages/browser-destinations/destinations/fullstory/src/__tests__/fullstoryV2.test.ts @@ -0,0 +1,277 @@ +import { Analytics, Context } from '@segment/analytics-next' +import fullstory from '..' +import trackEventV2 from '../trackEventV2' +import identifyUserV2 from '../identifyUserV2' +import viewedPageV2 from '../viewedPageV2' +import { FS as FSApi } from '../types' +import { Subscription } from '@segment/browser-destination-runtime/types' +import { defaultValues } from '@segment/actions-core/*' + +jest.mock('@fullstory/browser', () => ({ + ...jest.requireActual('@fullstory/browser'), + init: () => { + window.FS = jest.fn() as unknown as FSApi + } +})) + +const FakeOrgId = 'asdf-qwer' + +const example: Subscription[] = [ + { + partnerAction: 'trackEventV2', + name: 'Track Event', + enabled: true, + subscribe: 'type = "track"', + mapping: defaultValues(trackEventV2.fields) + }, + { + partnerAction: 'identifyUserV2', + name: 'Identify User', + enabled: true, + subscribe: 'type = "identify"', + mapping: defaultValues(identifyUserV2.fields) + }, + { + partnerAction: 'viewedPageV2', + name: 'Viewed Page', + enabled: true, + subscribe: 'type = "page"', + mapping: defaultValues(viewedPageV2.fields) + } +] + +describe('#track', () => { + it('sends record events to fullstory on "event"', async () => { + const [event] = await fullstory({ + orgId: FakeOrgId, + subscriptions: example + }) + + await event.load(Context.system(), {} as Analytics) + + await event.track?.( + new Context({ + type: 'track', + event: 'hello!', + properties: { + banana: '📞' + } + }) + ) + + expect(window.FS).toHaveBeenCalledWith( + 'trackEvent', + { + name: 'hello!', + properties: { + banana: '📞' + } + }, + 'segment-browser-actions' + ) + }) +}) + +describe('#identify', () => { + it('should default to anonymousId', async () => { + const [_, identifyUser] = await fullstory({ + orgId: FakeOrgId, + subscriptions: example + }) + + await identifyUser.load(Context.system(), {} as Analytics) + + await identifyUser.identify?.( + new Context({ + type: 'identify', + anonymousId: 'anon', + traits: { + testProp: false + } + }) + ) + + expect(window.FS).toHaveBeenCalledTimes(1) + expect(window.FS).toHaveBeenCalledWith( + 'setProperties', + { type: 'user', properties: { segmentAnonymousId: 'anon', testProp: false } }, + 'segment-browser-actions' + ) + }) + + it('should send an id', async () => { + const [_, identifyUser] = await fullstory({ + orgId: FakeOrgId, + subscriptions: example + }) + await identifyUser.load(Context.system(), {} as Analytics) + + await identifyUser.identify?.(new Context({ type: 'identify', userId: 'id' })) + expect(window.FS).toHaveBeenCalledWith('setIdentity', { uid: 'id', properties: {} }, 'segment-browser-actions') + }) + + it('can set user vars', async () => { + const [_, identifyUser] = await fullstory({ + orgId: FakeOrgId, + subscriptions: example + }) + + await identifyUser.load(Context.system(), {} as Analytics) + + await identifyUser.identify?.( + new Context({ + type: 'identify', + traits: { + name: 'Hasbulla', + email: 'thegoat@world', + height: '50cm' + } + }) + ) + + expect(window.FS).toHaveBeenCalledWith( + 'setProperties', + { + type: 'user', + properties: { + displayName: 'Hasbulla', + email: 'thegoat@world', + height: '50cm', + name: 'Hasbulla' + } + }, + 'segment-browser-actions' + ) + }) + + it('should set displayName correctly', async () => { + const [_, identifyUser] = await fullstory({ + orgId: FakeOrgId, + subscriptions: example + }) + + await identifyUser.load(Context.system(), {} as Analytics) + + await identifyUser.identify?.( + new Context({ + type: 'identify', + userId: 'userId', + traits: { + name: 'Hasbulla', + email: 'thegoat@world', + height: '50cm' + } + }) + ) + + expect(window.FS).toHaveBeenCalledWith( + 'setIdentity', + { + uid: 'userId', + properties: { + displayName: 'Hasbulla', + email: 'thegoat@world', + height: '50cm', + name: 'Hasbulla' + } + }, + 'segment-browser-actions' + ) + }) +}) + +describe('#page', () => { + it('sends page events to fullstory on "page" (category edition)', async () => { + const [, , viewed] = await fullstory({ + orgId: FakeOrgId, + subscriptions: example + }) + + await viewed.load(Context.system(), {} as Analytics) + + await viewed.page?.( + new Context({ + type: 'page', + category: 'Walruses', + name: 'Walrus Page', + properties: { + banana: '📞' + } + }) + ) + + expect(window.FS).toHaveBeenCalledWith( + 'setProperties', + { + type: 'page', + properties: { + pageName: 'Walruses', + banana: '📞' + } + }, + 'segment-browser-actions' + ) + }) + + it('sends page events to fullstory on "page" (name edition)', async () => { + const [, , viewed] = await fullstory({ + orgId: FakeOrgId, + subscriptions: example + }) + + await viewed.load(Context.system(), {} as Analytics) + + await viewed.page?.( + new Context({ + type: 'page', + name: 'Walrus Page', + properties: { + banana: '📞' + } + }) + ) + + expect(window.FS).toHaveBeenCalledWith( + 'setProperties', + { + type: 'page', + properties: { + pageName: 'Walrus Page', + banana: '📞' + } + }, + 'segment-browser-actions' + ) + }) + + it('sends page events to fullstory on "page" (no pageName edition)', async () => { + const [, , viewed] = await fullstory({ + orgId: FakeOrgId, + subscriptions: example + }) + + await viewed.load(Context.system(), {} as Analytics) + + await viewed.page?.( + new Context({ + type: 'page', + properties: { + banana: '📞', + keys: '🗝🔑' + } + }) + ) + + expect(window.FS).toHaveBeenCalledWith( + 'setProperties', + { + type: 'page', + properties: { + banana: '📞', + keys: '🗝🔑' + } + }, + 'segment-browser-actions' + ) + }) +}) diff --git a/packages/browser-destinations/destinations/fullstory/src/__tests__/initialization.test.ts b/packages/browser-destinations/destinations/fullstory/src/__tests__/initialization.test.ts new file mode 100644 index 0000000000..9f0274bc96 --- /dev/null +++ b/packages/browser-destinations/destinations/fullstory/src/__tests__/initialization.test.ts @@ -0,0 +1,81 @@ +import { Analytics, Context } from '@segment/analytics-next' +import fullstory, { destination } from '..' +import { Subscription } from '@segment/browser-destination-runtime/types' + +const example: Subscription[] = [ + { + partnerAction: 'trackEvent', + name: 'Track Event', + enabled: true, + subscribe: 'type = "track"', + mapping: { + name: { + '@path': '$.name' + }, + properties: { + '@path': '$.properties' + } + } + }, + { + partnerAction: 'identifyUser', + name: 'Identify User', + enabled: true, + subscribe: 'type = "identify"', + mapping: { + anonymousId: { + '@path': '$.anonymousId' + }, + userId: { + '@path': '$.userId' + }, + email: { + '@path': '$.traits.email' + }, + traits: { + '@path': '$.traits' + }, + displayName: { + '@path': '$.traits.name' + } + } + } +] + +test('can load fullstory', async () => { + const [event] = await fullstory({ + orgId: 'thefullstory.com', + subscriptions: example + }) + + jest.spyOn(destination.actions.trackEvent, 'perform') + jest.spyOn(destination, 'initialize') + + await event.load(Context.system(), {} as Analytics) + expect(destination.initialize).toHaveBeenCalled() + + const ctx = await event.track?.( + new Context({ + type: 'track', + properties: { + banana: '📞' + } + }) + ) + + expect(destination.actions.trackEvent.perform).toHaveBeenCalled() + expect(ctx).not.toBeUndefined() + + const scripts = window.document.querySelectorAll('script') + expect(scripts).toMatchInlineSnapshot(` + NodeList [ + , + ] + `) +}) diff --git a/packages/browser-destinations/destinations/fullstory/src/identifyUserV2/generated-types.ts b/packages/browser-destinations/destinations/fullstory/src/identifyUserV2/generated-types.ts new file mode 100644 index 0000000000..e325fbc445 --- /dev/null +++ b/packages/browser-destinations/destinations/fullstory/src/identifyUserV2/generated-types.ts @@ -0,0 +1,26 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * The user's id + */ + userId?: string + /** + * The user's anonymous id + */ + anonymousId?: string + /** + * The user's display name + */ + displayName?: string + /** + * The user's email + */ + email?: string + /** + * The Segment traits to be forwarded to FullStory + */ + traits?: { + [k: string]: unknown + } +} diff --git a/packages/browser-destinations/destinations/fullstory/src/identifyUserV2/index.ts b/packages/browser-destinations/destinations/fullstory/src/identifyUserV2/index.ts new file mode 100644 index 0000000000..97e7db1e7f --- /dev/null +++ b/packages/browser-destinations/destinations/fullstory/src/identifyUserV2/index.ts @@ -0,0 +1,95 @@ +import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' +import type { FS } from '../types' +import { segmentEventSource } from '..' + +// Change from unknown to the partner SDK types +const action: BrowserActionDefinition = { + title: 'Identify User V2', + description: 'Sets user identity properties', + platform: 'web', + defaultSubscription: 'type = "identify"', + fields: { + userId: { + type: 'string', + required: false, + description: "The user's id", + label: 'User ID', + default: { + '@path': '$.userId' + } + }, + anonymousId: { + type: 'string', + required: false, + description: "The user's anonymous id", + label: 'Anonymous ID', + default: { + '@path': '$.anonymousId' + } + }, + displayName: { + type: 'string', + required: false, + description: "The user's display name", + label: 'Display Name', + default: { + '@path': '$.traits.name' + } + }, + email: { + type: 'string', + required: false, + description: "The user's email", + label: 'Email', + default: { + '@path': '$.traits.email' + } + }, + traits: { + type: 'object', + required: false, + description: 'The Segment traits to be forwarded to FullStory', + label: 'Traits', + default: { + '@path': '$.traits' + } + } + }, + perform: (FS, event) => { + const newTraits: Record = event.payload.traits || {} + + if (event.payload.anonymousId) { + newTraits.segmentAnonymousId = event.payload.anonymousId + } + + const userProperties = { + ...newTraits, + ...(event.payload.email !== undefined && { email: event.payload.email }), + ...(event.payload.displayName !== undefined && { displayName: event.payload.displayName }) + } + + if (event.payload.userId) { + FS( + 'setIdentity', + { + uid: event.payload.userId, + properties: userProperties + }, + segmentEventSource + ) + } else { + FS( + 'setProperties', + { + type: 'user', + properties: userProperties + }, + segmentEventSource + ) + } + } +} + +export default action diff --git a/packages/browser-destinations/destinations/fullstory/src/index.ts b/packages/browser-destinations/destinations/fullstory/src/index.ts index c7494cabe4..01b1ac4d75 100644 --- a/packages/browser-destinations/destinations/fullstory/src/index.ts +++ b/packages/browser-destinations/destinations/fullstory/src/index.ts @@ -1,11 +1,14 @@ import type { FS } from './types' import type { BrowserDestinationDefinition } from '@segment/browser-destination-runtime/types' -import { FSPackage } from './types' +import { initFullStory } from './types' import { browserDestination } from '@segment/browser-destination-runtime/shim' import type { Settings } from './generated-types' import trackEvent from './trackEvent' +import trackEventV2 from './trackEventV2' import identifyUser from './identifyUser' +import identifyUserV2 from './identifyUserV2' import viewedPage from './viewedPage' +import viewedPageV2 from './viewedPageV2' import { defaultValues } from '@segment/actions-core' declare global { @@ -24,15 +27,22 @@ export const destination: BrowserDestinationDefinition = { { name: 'Track Event', subscribe: 'type = "track"', - partnerAction: 'trackEvent', - mapping: defaultValues(trackEvent.fields), + partnerAction: 'trackEventV2', + mapping: defaultValues(trackEventV2.fields), type: 'automatic' }, { name: 'Identify User', subscribe: 'type = "identify"', - partnerAction: 'identifyUser', - mapping: defaultValues(identifyUser.fields), + partnerAction: 'identifyUserV2', + mapping: defaultValues(identifyUserV2.fields), + type: 'automatic' + }, + { + name: 'Viewed Page', + subscribe: 'type = "page"', + partnerAction: 'viewedPageV2', + mapping: defaultValues(viewedPageV2.fields), type: 'automatic' } ], @@ -60,11 +70,14 @@ export const destination: BrowserDestinationDefinition = { }, actions: { trackEvent, + trackEventV2, identifyUser, - viewedPage + identifyUserV2, + viewedPage, + viewedPageV2 }, initialize: async ({ settings }, dependencies) => { - FSPackage.init(settings) + initFullStory(settings) await dependencies.resolveWhen(() => Object.prototype.hasOwnProperty.call(window, 'FS'), 100) return window.FS } diff --git a/packages/browser-destinations/destinations/fullstory/src/trackEventV2/generated-types.ts b/packages/browser-destinations/destinations/fullstory/src/trackEventV2/generated-types.ts new file mode 100644 index 0000000000..6ec21ec140 --- /dev/null +++ b/packages/browser-destinations/destinations/fullstory/src/trackEventV2/generated-types.ts @@ -0,0 +1,14 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * The name of the event. + */ + name: string + /** + * A JSON object containing additional information about the event that will be indexed by FullStory. + */ + properties?: { + [k: string]: unknown + } +} diff --git a/packages/browser-destinations/destinations/fullstory/src/trackEventV2/index.ts b/packages/browser-destinations/destinations/fullstory/src/trackEventV2/index.ts new file mode 100644 index 0000000000..50f2f7b2c0 --- /dev/null +++ b/packages/browser-destinations/destinations/fullstory/src/trackEventV2/index.ts @@ -0,0 +1,44 @@ +import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' +import type { FS } from '../types' +import { segmentEventSource } from '..' + +const action: BrowserActionDefinition = { + title: 'Track Event V2', + description: 'Track events', + platform: 'web', + defaultSubscription: 'type = "track"', + fields: { + name: { + description: 'The name of the event.', + label: 'Name', + required: true, + type: 'string', + default: { + '@path': '$.event' + } + }, + properties: { + description: 'A JSON object containing additional information about the event that will be indexed by FullStory.', + label: 'Properties', + required: false, + type: 'object', + default: { + '@path': '$.properties' + } + } + }, + perform: (FS, event) => { + FS( + 'trackEvent', + { + name: event.payload.name, + properties: event.payload.properties ?? {} + }, + segmentEventSource + ) + } +} + +export default action diff --git a/packages/browser-destinations/destinations/fullstory/src/types.ts b/packages/browser-destinations/destinations/fullstory/src/types.ts index fa6f59e129..7a23e1a0e5 100644 --- a/packages/browser-destinations/destinations/fullstory/src/types.ts +++ b/packages/browser-destinations/destinations/fullstory/src/types.ts @@ -1,10 +1,4 @@ -import * as FullStory from '@fullstory/browser' +import { FullStory, init as initFullStory } from '@fullstory/browser' -export const FSPackage = FullStory -export type FS = typeof FullStory & { - // setVars is not available on the FS client yet. - setVars: (eventName: string, eventProperties: object, source: string) => {} - setUserVars: (eventProperties: object, source: string) => void - event: (eventName: string, eventProperties: { [key: string]: unknown }, source: string) => void - identify: (uid: string, customVars: FullStory.UserVars, source: string) => void -} +export type FS = typeof FullStory +export { FullStory, initFullStory } diff --git a/packages/browser-destinations/destinations/fullstory/src/viewedPageV2/generated-types.ts b/packages/browser-destinations/destinations/fullstory/src/viewedPageV2/generated-types.ts new file mode 100644 index 0000000000..fa90a6ee12 --- /dev/null +++ b/packages/browser-destinations/destinations/fullstory/src/viewedPageV2/generated-types.ts @@ -0,0 +1,14 @@ +// Generated file. DO NOT MODIFY IT BY HAND. + +export interface Payload { + /** + * The name of the page that was viewed. + */ + pageName?: string + /** + * The properties of the page that was viewed. + */ + properties?: { + [k: string]: unknown + } +} diff --git a/packages/browser-destinations/destinations/fullstory/src/viewedPageV2/index.ts b/packages/browser-destinations/destinations/fullstory/src/viewedPageV2/index.ts new file mode 100644 index 0000000000..a531c0df22 --- /dev/null +++ b/packages/browser-destinations/destinations/fullstory/src/viewedPageV2/index.ts @@ -0,0 +1,52 @@ +import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types' +import type { Settings } from '../generated-types' +import type { Payload } from './generated-types' +import type { FS } from '../types' +import { segmentEventSource } from '..' + +const action: BrowserActionDefinition = { + title: 'Viewed Page V2', + description: 'Sets page properties', + defaultSubscription: 'type = "page"', + platform: 'web', + fields: { + pageName: { + type: 'string', + required: false, + description: 'The name of the page that was viewed.', + label: 'Page Name', + default: { + '@if': { + exists: { '@path': '$.category' }, + then: { '@path': '$.category' }, + else: { '@path': '$.name' } + } + } + }, + properties: { + type: 'object', + required: false, + description: 'The properties of the page that was viewed.', + label: 'Properties', + default: { + '@path': '$.properties' + } + } + }, + perform: (FS, event) => { + const properties: object = event.payload.pageName + ? { pageName: event.payload.pageName, ...event.payload.properties } + : { ...event.payload.properties } + + FS( + 'setProperties', + { + type: 'page', + properties + }, + segmentEventSource + ) + } +} + +export default action diff --git a/yarn.lock b/yarn.lock index e892fc43f7..b9cc34ba4b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1256,17 +1256,17 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9" integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A== -"@fullstory/browser@^1.4.9": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@fullstory/browser/-/browser-1.7.1.tgz#eb94fcb5e21b13a1b30de58951480ac344e61cdd" - integrity sha512-IBPisG+xRyTHHX8XkZJkQRbP2hVYNMZUYW8R3YiB582dl/VZImkFN+LopIAfPqB97FAZgUTofi7flkrHT4Qmtg== +"@fullstory/browser@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@fullstory/browser/-/browser-2.0.3.tgz#09c0b590d81a8098f8fd85d160a44e4f73e15bfb" + integrity sha512-usjH8FB1O2LiSWoblsuKhFhlYDGpIPuyQVOx4JXtxm9QmQARdKZdNq1vPijxuDvOGjhwtVZa4JmhvByRRuDPnQ== dependencies: - "@fullstory/snippet" "1.3.1" + "@fullstory/snippet" "2.0.3" -"@fullstory/snippet@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@fullstory/snippet/-/snippet-1.3.1.tgz#6817ea94601e071e630b25262e703ca356a5f537" - integrity sha512-NgrBWGHH5i8zejlRFSyJNhovkNqHAXsWKrcXIWaABrgESwbkdGETjOU7BD7d1ZeT0X+QXL/2yr/1y4xnWfVkwQ== +"@fullstory/snippet@2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@fullstory/snippet/-/snippet-2.0.3.tgz#d5410132becc3d0115bb129b57461d228c73b5f0" + integrity sha512-EaCuTQSLv5FvnjHLbTxErn3sS1+nLqf1p6sA/c4PV49stBtkUakA0eLhJJdaw0WLdXyEzZXf86lRNsjEzrgGPw== "@gar/promisify@^1.1.3": version "1.1.3"