Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V7: Add methods to pause and resume sessions #666

Merged
merged 4 commits into from
Dec 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
- Add `onBreadcrumb` and `onSession` callbacks. [#665](https://github.com/bugsnag/bugsnag-js/pull/665)
- Refactor `notify()` to not accept events (they go via `_notify()` instead). Consolidate `Event` static methods into a single `.create()` utility, used by all automatic errors detection components. [#664](https://github.com/bugsnag/bugsnag-js/pull/664)
- Refactor `notify()` to not accept events (they go via `_notify()` instead). Consolidate `Event` static methods into a single `.create()` utility, used by all automatic error detection components. [#664](https://github.com/bugsnag/bugsnag-js/pull/664)
- Add methods to pause and resume sessions [#666](https://github.com/bugsnag/bugsnag-js/pull/666)
bengourley marked this conversation as resolved.
Show resolved Hide resolved
- Add `pauseSession()` and `resumeSession()` methods to `Client` [#666](https://github.com/bugsnag/bugsnag-js/pull/666)

## 6.4.3 (2019-10-21)

Expand Down
8 changes: 8 additions & 0 deletions packages/core/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ class BugsnagClient {
this._cbs.b = filter(this._cbs.b, f => f !== fn)
}

pauseSession () {
return this._sessionDelegate.pauseSession(this)
}

resumeSession () {
return this._sessionDelegate.resumeSession(this)
}

leaveBreadcrumb (message, metadata, type) {
// coerce bad values so that the defaults get set
message = typeof message === 'string' ? message : ''
Expand Down
1 change: 1 addition & 0 deletions packages/core/lib/clone-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module.exports = (client) => {

clone._logger = client._logger
clone._delivery = client._delivery
clone._sessionDelegate = client._sessionDelegate

return clone
}
24 changes: 24 additions & 0 deletions packages/core/test/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,30 @@ describe('@bugsnag/core/client', () => {
})
})

describe('pause/resumeSession()', () => {
it('forwards on calls to the session delegate', () => {
const client = new Client({ apiKey: 'API_KEY' })
const sessionDelegate = {
startSession: () => {},
pauseSession: () => {},
resumeSession: () => {}
}
client._sessionDelegate = sessionDelegate

const startSpy = spyOn(sessionDelegate, 'startSession')
const pauseSpy = spyOn(sessionDelegate, 'pauseSession')
const resumeSpy = spyOn(sessionDelegate, 'resumeSession')
client._sessionDelegate = sessionDelegate

client.startSession()
expect(startSpy).toHaveBeenCalledTimes(1)
client.pauseSession()
expect(pauseSpy).toHaveBeenCalledTimes(1)
client.resumeSession()
expect(resumeSpy).toHaveBeenCalledTimes(1)
})
})

describe('getUser() / setUser()', () => {
it('sets and retrieves user properties', () => {
const c = new Client({ apiKey: 'aaaa' })
Expand Down
6 changes: 5 additions & 1 deletion packages/core/types/client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ declare class Client {
public getUser(): { id?: string; email?: string; name?: string };
public setUser(id?: string, email?: string, name?: string): void;

// sessions
public startSession(): Client;
public pauseSession(): void;
public resumeSession(): Client;

public use(plugin: common.Plugin, ...args: any[]): Client;
public getPlugin(name: string): any;
public notify(
Expand All @@ -35,7 +40,6 @@ declare class Client {
cb?: (err: any, event: Event) => void,
): void;
public leaveBreadcrumb(message: string, metadata?: { [key: string]: common.BreadcrumbMetadataValue }, type?: string): void;
public startSession(): Client;
}

export default Client;
14 changes: 14 additions & 0 deletions packages/plugin-browser-session/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const sessionDelegate = {
startSession: (client, session) => {
const sessionClient = client
sessionClient._session = session
sessionClient._pausedSession = null

const releaseStage = inferReleaseStage(sessionClient)

Expand All @@ -32,5 +33,18 @@ const sessionDelegate = {
})

return sessionClient
},
resumeSession: (client) => {
if (client._pausedSession) {
client._session = client._pausedSession
client._pausedSession = null
return client
} else {
return client.startSession()
}
},
pauseSession: (client) => {
client._pausedSession = client._session
client._session = null
}
}
38 changes: 38 additions & 0 deletions packages/plugin-browser-session/test/session.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,42 @@ describe('plugin: sessions', () => {
/"endpoints" should be an object containing endpoint URLs { notify, sessions }/
)
})

it('supports pausing and resuming sessions', (done) => {
const payloads = []
const c = new Client({
apiKey: 'API_KEY'
})
c.use(plugin)
c._setDelivery(client => ({
sendEvent: (p, cb = () => {}) => {
payloads.push(p)
cb()
},
sendSession: (p, cb = () => {}) => cb()
}))
c.notify(new Error('1'))
c.startSession()
c.notify(new Error('2'))
c.pauseSession()
c.notify(new Error('3'))
c.resumeSession()
c.notify(new Error('4'))
c.startSession()
c.notify(new Error('5'))
c._pausedSession = c._session = null
c.resumeSession()
c.notify(new Error('6'))

setTimeout(() => {
expect(payloads.length).toBe(6)
expect(payloads[0].events[0].session).toBe(undefined)
expect(payloads[1].events[0].session).toBeDefined()
expect(payloads[2].events[0].session).toBe(undefined)
expect(payloads[3].events[0].session.id).toBe(payloads[1].events[0].session.id)
expect(payloads[4].events[0].session.id).not.toBe(payloads[3].events[0].session.id)
expect(payloads[5].events[0].session.id).not.toBe(payloads[4].events[0].session.id)
done()
}, 0)
})
})
14 changes: 14 additions & 0 deletions packages/plugin-server-session/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,22 @@ module.exports = {
startSession: (client, session) => {
const sessionClient = clone(client)
sessionClient._session = session
sessionClient._pausedSession = null
sessionTracker.track(sessionClient._session)
return sessionClient
},
pauseSession: (client) => {
client._pausedSession = client._session
client._session = null
},
resumeSession: (client) => {
if (client._pausedSession) {
client._session = client._pausedSession
client._pausedSession = null
return client
} else {
return client.startSession()
}
}
}
},
Expand Down
36 changes: 36 additions & 0 deletions packages/plugin-server-session/test/session.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,40 @@ describe('plugin: server sessions', () => {
expect(sessionClient.breadcrumbs.length).toBe(2)
expect(Object.keys(sessionClient._metadata).length).toBe(2)
})

it('should support pausing/resuming sessions', () => {
bengourley marked this conversation as resolved.
Show resolved Hide resolved
class TrackerMock extends Emitter {
start () {}
stop () {}
track () {}
}
const plugin = proxyquire('../session', { './tracker': TrackerMock })

const c = new Client({ apiKey: 'aaaa-aaaa-aaaa-aaaa' })
c.use(plugin)

// start a session and get its id
const sessionClient = c.startSession()
const sid0 = sessionClient._session.id

// ensure pausing the session clears the client._session property
sessionClient.pauseSession()
const s1 = sessionClient._session
const psid1 = sessionClient._pausedSession.id
expect(s1).toBe(null)
expect(psid1).toBe(sid0)

// ensure resuming the session gets back the original session (not a new one)
sessionClient.resumeSession()
const sid2 = sessionClient._session.id
expect(sid2).toBe(sid0)

// ensure resumeSession() starts a new one when no paused session exists
sessionClient._session = null
sessionClient._pausedSession = null
const resumedClient = sessionClient.resumeSession()
expect(resumedClient._session).toBeTruthy()
const sid3 = resumedClient._session.id
expect(sid3).not.toBe(sid0)
})
})