Skip to content

Commit

Permalink
RFC - Storage Layer for Plugins V2 💿 (#638)
Browse files Browse the repository at this point in the history
  • Loading branch information
pooyaj authored Dec 8, 2022
1 parent 0d70637 commit e16017d
Show file tree
Hide file tree
Showing 13 changed files with 477 additions and 145 deletions.
5 changes: 5 additions & 0 deletions .changeset/tall-ligers-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@segment/analytics-next': minor
---

Creating universal storage layer and passing it to plugins
2 changes: 1 addition & 1 deletion packages/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"size-limit": [
{
"path": "dist/umd/index.js",
"limit": "27.3 KB"
"limit": "28.0 KB"
}
],
"dependencies": {
Expand Down
9 changes: 7 additions & 2 deletions packages/browser/src/browser/__tests__/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ import * as SegmentPlugin from '../../plugins/segmentio'
import jar from 'js-cookie'
import { PriorityQueue } from '../../lib/priority-queue'
import { getCDN, setGlobalCDNUrl } from '../../lib/parse-cdn'
import { UniversalStorage } from '../../core/user'
import { clearAjsBrowserStorage } from '../../test-helpers/browser-storage'
import { ActionDestination } from '@/plugins/remote-loader'

const storage = {} as UniversalStorage

let fetchCalls: Array<any>[] = []

jest.mock('unfetch', () => {
Expand Down Expand Up @@ -879,7 +882,8 @@ describe('retries', () => {
throw new Error('aaay')
},
},
ajs
ajs,
storage
)

// Dispatching an event will push it into the priority queue.
Expand Down Expand Up @@ -907,7 +911,8 @@ describe('retries', () => {
ready: () => Promise.resolve(true),
track: (ctx) => ctx,
},
ajs
ajs,
storage
)

// @ts-ignore ignore reassining function
Expand Down
23 changes: 21 additions & 2 deletions packages/browser/src/core/analytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ import {
} from '../events'
import { Plugin } from '../plugin'
import { EventQueue } from '../queue/event-queue'
import { CookieOptions, Group, ID, User, UserOptions } from '../user'
import {
CookieOptions,
getAvailableStorageOptions,
Group,
ID,
UniversalStorage,
User,
UserOptions,
} from '../user'
import autoBind from '../../lib/bind-all'
import { PersistedPriorityQueue } from '../../lib/priority-queue/persisted'
import type { LegacyDestination } from '../../plugins/ajs-destination'
Expand Down Expand Up @@ -102,6 +110,9 @@ export class Analytics
private _group: Group
private eventFactory: EventFactory
private _debug = false
private _universalStorage: UniversalStorage<{
[k: string]: unknown
}>

initialized = false
integrations: Integrations
Expand All @@ -122,6 +133,14 @@ export class Analytics
this.settings.timeout = this.settings.timeout ?? 300
this.queue =
queue ?? createDefaultQueue(options?.retryQueue, disablePersistance)

this._universalStorage = new UniversalStorage(
disablePersistance !== false
? ['localStorage', 'cookie', 'memory']
: ['memory'],
getAvailableStorageOptions(cookieOptions)
)

this._user =
user ??
new User(
Expand Down Expand Up @@ -304,7 +323,7 @@ export class Analytics
const ctx = Context.system()

const registrations = plugins.map((xt) =>
this.queue.register(ctx, xt, this)
this.queue.register(ctx, xt, this, this._universalStorage)
)
await Promise.all(registrations)

Expand Down
6 changes: 4 additions & 2 deletions packages/browser/src/core/plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Analytics } from '../analytics'
import { Context } from '../context'
import { UniversalStorage } from '../user'

interface PluginConfig {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options: any
priority: 'critical' | 'non-critical' // whether AJS should expect this plugin to be loaded before starting event delivery
options?: any
priority?: 'critical' | 'non-critical' // whether AJS should expect this plugin to be loaded before starting event delivery
storage?: UniversalStorage
}

// enrichment - modifies the event. Enrichment can happen in parallel, by reducing all changes in the final event. Failures in this stage could halt event delivery.
Expand Down
79 changes: 47 additions & 32 deletions packages/browser/src/core/queue/__tests__/event-queue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import { Plugin } from '../../plugin'
import { EventQueue } from '../event-queue'
import { pTimeout } from '../../callback'
import { ActionDestination } from '../../../plugins/remote-loader'
import { UniversalStorage } from '../../user'

const storage = {} as UniversalStorage

async function flushAll(eq: EventQueue): Promise<Context[]> {
const flushSpy = jest.spyOn(eq, 'flush')
Expand Down Expand Up @@ -149,7 +152,8 @@ describe('Flushing', () => {
return Promise.resolve(ctx)
},
},
ajs
ajs,
storage
)

eq.dispatch(fruitBasket)
Expand Down Expand Up @@ -219,7 +223,8 @@ describe('Flushing', () => {
return Promise.resolve(ctx)
},
},
ajs
ajs,
storage
)

eq.dispatch(fruitBasket)
Expand Down Expand Up @@ -257,7 +262,8 @@ describe('Flushing', () => {
return ctx
},
},
ajs
ajs,
storage
)

const dispatches = [
Expand Down Expand Up @@ -294,7 +300,8 @@ describe('Flushing', () => {
return ctx
},
},
ajs
ajs,
storage
)

const context = await eq.dispatchSingle(fruitBasket)
Expand All @@ -321,7 +328,8 @@ describe('Flushing', () => {
return Promise.resolve(ctx)
},
},
ajs
ajs,
storage
)

eq.dispatch(fruitBasket)
Expand Down Expand Up @@ -362,7 +370,8 @@ describe('Flushing', () => {
return Promise.resolve(ctx)
},
},
ajs
ajs,
storage
)

const fruitBasketDelivery = eq.dispatch(fruitBasket)
Expand Down Expand Up @@ -429,9 +438,9 @@ describe('Flushing', () => {

const ctx = new Context(evt)

await eq.register(Context.system(), amplitude, ajs)
await eq.register(Context.system(), mixPanel, ajs)
await eq.register(Context.system(), segmentio, ajs)
await eq.register(Context.system(), amplitude, ajs, storage)
await eq.register(Context.system(), mixPanel, ajs, storage)
await eq.register(Context.system(), segmentio, ajs, storage)

eq.dispatch(ctx)

Expand Down Expand Up @@ -462,9 +471,9 @@ describe('Flushing', () => {

const ctx = new Context(evt)

await eq.register(Context.system(), amplitude, ajs)
await eq.register(Context.system(), mixPanel, ajs)
await eq.register(Context.system(), segmentio, ajs)
await eq.register(Context.system(), amplitude, ajs, storage)
await eq.register(Context.system(), mixPanel, ajs, storage)
await eq.register(Context.system(), segmentio, ajs, storage)

eq.dispatch(ctx)

Expand Down Expand Up @@ -496,9 +505,9 @@ describe('Flushing', () => {

const ctx = new Context(evt)

await eq.register(Context.system(), amplitude, ajs)
await eq.register(Context.system(), mixPanel, ajs)
await eq.register(Context.system(), segmentio, ajs)
await eq.register(Context.system(), amplitude, ajs, storage)
await eq.register(Context.system(), mixPanel, ajs, storage)
await eq.register(Context.system(), segmentio, ajs, storage)

eq.dispatch(ctx)

Expand Down Expand Up @@ -530,9 +539,9 @@ describe('Flushing', () => {

const ctx = new Context(evt)

await eq.register(Context.system(), amplitude, ajs)
await eq.register(Context.system(), mixPanel, ajs)
await eq.register(Context.system(), segmentio, ajs)
await eq.register(Context.system(), amplitude, ajs, storage)
await eq.register(Context.system(), mixPanel, ajs, storage)
await eq.register(Context.system(), segmentio, ajs, storage)

eq.dispatch(ctx)

Expand Down Expand Up @@ -563,9 +572,9 @@ describe('Flushing', () => {

const ctx = new Context(evt)

await eq.register(Context.system(), amplitude, ajs)
await eq.register(Context.system(), mixPanel, ajs)
await eq.register(Context.system(), segmentio, ajs)
await eq.register(Context.system(), amplitude, ajs, storage)
await eq.register(Context.system(), mixPanel, ajs, storage)
await eq.register(Context.system(), segmentio, ajs, storage)

eq.dispatch(ctx)

Expand Down Expand Up @@ -598,8 +607,8 @@ describe('Flushing', () => {

const ctx = new Context(evt)

await eq.register(Context.system(), amplitude, ajs)
await eq.register(Context.system(), segmentio, ajs)
await eq.register(Context.system(), amplitude, ajs, storage)
await eq.register(Context.system(), segmentio, ajs, storage)

eq.dispatch(ctx)

Expand Down Expand Up @@ -632,8 +641,8 @@ describe('Flushing', () => {

const ctx = new Context(evt)

await eq.register(Context.system(), fullstory, ajs)
await eq.register(Context.system(), segmentio, ajs)
await eq.register(Context.system(), fullstory, ajs, storage)
await eq.register(Context.system(), segmentio, ajs, storage)

eq.dispatch(ctx)

Expand Down Expand Up @@ -663,9 +672,9 @@ describe('Flushing', () => {
}

const ctx = new Context(evt)
await eq.register(Context.system(), amplitude, ajs)
await eq.register(Context.system(), mixPanel, ajs)
await eq.register(Context.system(), segmentio, ajs)
await eq.register(Context.system(), amplitude, ajs, storage)
await eq.register(Context.system(), mixPanel, ajs, storage)
await eq.register(Context.system(), segmentio, ajs, storage)
await eq.dispatch(ctx)

const skipAmplitudeAndSegment: MiddlewareFunction = ({
Expand All @@ -684,7 +693,8 @@ describe('Flushing', () => {
await eq.register(
Context.system(),
sourceMiddlewarePlugin(skipAmplitudeAndSegment, {}),
ajs
ajs,
storage
)

await eq.dispatch(ctx)
Expand All @@ -702,7 +712,9 @@ describe('deregister', () => {
const toBeRemoved = { ...testPlugin, name: 'remove-me' }
const plugins = [testPlugin, toBeRemoved]

const promises = plugins.map((p) => eq.register(Context.system(), p, ajs))
const promises = plugins.map((p) =>
eq.register(Context.system(), p, ajs, storage)
)
await Promise.all(promises)

await eq.deregister(Context.system(), toBeRemoved, ajs)
Expand All @@ -715,7 +727,9 @@ describe('deregister', () => {
const toBeRemoved = { ...testPlugin, name: 'remove-me', unload: jest.fn() }
const plugins = [testPlugin, toBeRemoved]

const promises = plugins.map((p) => eq.register(Context.system(), p, ajs))
const promises = plugins.map((p) =>
eq.register(Context.system(), p, ajs, storage)
)
await Promise.all(promises)

await eq.deregister(Context.system(), toBeRemoved, ajs)
Expand Down Expand Up @@ -778,7 +792,8 @@ describe('dispatchSingle', () => {
return Promise.resolve(ctx)
},
},
ajs
ajs,
storage
)

expect(eq.queue.length).toBe(0)
Expand Down
Loading

0 comments on commit e16017d

Please sign in to comment.