diff --git a/README.md b/README.md
index f782025..1c882b4 100644
--- a/README.md
+++ b/README.md
@@ -50,6 +50,7 @@ account.on('signout', redirectToHome)
- [account.one](#accountone)
- [account.off](#accountoff)
- [Events](#events)
+- [Hooks](#hooks)
- [Requests](#requests)
### Constructor
@@ -994,6 +995,42 @@ hoodie.off('connectionstatus:disconnected', showNotification)
+### Hooks
+
+```js
+// clear user’s local store signin and after signout
+account.hook.before('signin', function (options) {
+ return localUserStore.clear()
+})
+account.hook.after('signout', function (options) {
+ return localUserStore.clear()
+})
+```
+
+
+
+
+
+ Hook
+ |
+
+ Arguments
+ |
+
+
+
+ signin |
+ options as they were passed into account.signIn(options) |
+
+
+ signout |
+ {} |
+
+
+
+See [before-after-hook](https://www.npmjs.com/package/before-after-hook) for
+more information.
+
### Requests
Hoodie comes with a list of built-in account requests, which can be disabled,
diff --git a/admin/index.js b/admin/index.js
index 19baa9a..ffa9937 100644
--- a/admin/index.js
+++ b/admin/index.js
@@ -1,6 +1,7 @@
module.exports = AccountAdmin
var EventEmitter = require('events').EventEmitter
+var Hook = require('before-after-hook')
var getAccount = require('../utils/get-account')
@@ -36,7 +37,8 @@ function AccountAdmin (options) {
cacheKey: cacheKey,
emitter: emitter,
account: getAccount({cacheKey: cacheKey}),
- url: options.url
+ url: options.url,
+ hook: new Hook()
}
var admin = {
@@ -68,7 +70,8 @@ function AccountAdmin (options) {
on: events.on.bind(null, state),
one: events.one.bind(null, state),
- off: events.off.bind(null, state)
+ off: events.off.bind(null, state),
+ hook: state.hook.api
}
// sessions.add can use accounts.find to lookup user id by username
diff --git a/index.js b/index.js
index c5ef7aa..c2bd219 100644
--- a/index.js
+++ b/index.js
@@ -38,6 +38,7 @@ function Account (options) {
on: events.on.bind(null, state),
one: events.one.bind(null, state),
off: events.off.bind(null, state),
+ hook: state.hook.api,
validate: require('./lib/validate').bind(null, state)
}
}
diff --git a/lib/sign-in.js b/lib/sign-in.js
index d9816f5..e55b2bd 100644
--- a/lib/sign-in.js
+++ b/lib/sign-in.js
@@ -3,7 +3,6 @@ module.exports = signIn
var Promise = require('lie')
var clone = require('lodash/clone')
var get = require('lodash/get')
-var invokeMap = require('lodash/invokeMap')
var internals = module.exports.internals = {}
internals.deserialise = require('../utils/deserialise')
@@ -16,77 +15,50 @@ function signIn (state, options) {
return Promise.reject(new Error('options.username and options.password is required'))
}
- var preHooks = []
- // note: the `pre:signin` & `post:signin` events are not considered public
- // APIs and might change in future without notice
- // https://github.com/hoodiehq/hoodie-account-client/issues/65
- state.emitter.emit('pre:signin', { hooks: preHooks })
-
- return Promise.resolve()
-
- .then(function () {
- return Promise.all(invokeMap(preHooks, 'call'))
- })
-
- .then(function () {
+ return state.hook('signin', options, function (options) {
return internals.request({
url: state.url + '/session',
method: 'PUT',
body: internals.serialise('session', options)
})
- })
- .then(function (response) {
- var data = internals.deserialise(response.body, {
- include: 'account'
- })
+ .then(function (response) {
+ var data = internals.deserialise(response.body, {
+ include: 'account'
+ })
- // admins don’t have an account
- if (!data.account) {
- data.account = {
- username: options.username
+ // admins don’t have an account
+ if (!data.account) {
+ data.account = {
+ username: options.username
+ }
}
- }
-
- // If the username hasn’t changed, emit 'reauthenticate' instead of 'signin'
- var emitEvent = 'signin'
- if (get(state, 'account.username') === options.username) {
- emitEvent = 'reauthenticate'
- }
- state.account = {
- username: data.account.username,
- session: {
- id: data.id
+ // If the username hasn’t changed, emit 'reauthenticate' instead of 'signin'
+ var emitEvent = 'signin'
+ if (get(state, 'account.username') === options.username) {
+ emitEvent = 'reauthenticate'
}
- }
- if (data.account.id) {
- state.account.id = data.account.id
- }
-
- internals.saveAccount({
- cacheKey: state.cacheKey,
- account: state.account
- })
-
- state.emitter.emit(emitEvent, clone(state.account))
-
- return data.account
- })
+ state.account = {
+ username: data.account.username,
+ session: {
+ id: data.id
+ }
+ }
- .then(function (account) {
- var postHooks = []
+ if (data.account.id) {
+ state.account.id = data.account.id
+ }
- // note: the `pre:signin` & `post:signin` events are not considered public
- // APIs and might change in future without notice
- // https://github.com/hoodiehq/hoodie-account-client/issues/65
- state.emitter.emit('post:signin', { hooks: postHooks })
+ internals.saveAccount({
+ cacheKey: state.cacheKey,
+ account: state.account
+ })
- return Promise.all(invokeMap(postHooks, 'call'))
+ state.emitter.emit(emitEvent, clone(state.account))
- .then(function () {
- return clone(account)
+ return data.account
})
})
}
diff --git a/lib/sign-out.js b/lib/sign-out.js
index 116bdee..500785f 100644
--- a/lib/sign-out.js
+++ b/lib/sign-out.js
@@ -1,7 +1,6 @@
module.exports = signOut
var clone = require('lodash/clone')
-var invokeMap = require('lodash/invokeMap')
var internals = module.exports.internals = {}
internals.request = require('../utils/request')
@@ -16,19 +15,7 @@ function signOut (state) {
var accountProperties = internals.get(state)
- var preHooks = []
- // note: the `pre:signout` & `post:signout` events are not considered public
- // APIs and might change in future without notice
- // https://github.com/hoodiehq/hoodie-account-client/issues/65
- state.emitter.emit('pre:signout', { hooks: preHooks })
-
- return Promise.resolve()
-
- .then(function () {
- return Promise.all(invokeMap(preHooks, 'call'))
- })
-
- .then(function () {
+ return state.hook('signout', function () {
return internals.request({
method: 'DELETE',
url: state.url + '/session',
@@ -36,29 +23,17 @@ function signOut (state) {
authorization: 'Session ' + state.account.session.id
}
})
- })
-
- .then(function () {
- internals.clearSession({
- cacheKey: state.cacheKey
- })
-
- var accountClone = clone(state.account)
-
- delete state.account
- state.emitter.emit('signout', accountClone)
-
- var postHooks = []
+ .then(function () {
+ internals.clearSession({
+ cacheKey: state.cacheKey
+ })
- // note: the `pre:signout` & `post:signout` events are not considered public
- // APIs and might change in future without notice
- // https://github.com/hoodiehq/hoodie-account-client/issues/65
- state.emitter.emit('post:signout', { hooks: postHooks })
+ var accountClone = clone(state.account)
+ delete state.account
- return Promise.all(invokeMap(postHooks, 'call'))
+ state.emitter.emit('signout', accountClone)
- .then(function () {
return clone(accountProperties)
})
})
diff --git a/package.json b/package.json
index 144f870..8892a63 100644
--- a/package.json
+++ b/package.json
@@ -62,6 +62,7 @@
"uglify-js": "^2.4.24"
},
"dependencies": {
+ "before-after-hook": "^1.0.1",
"humble-localstorage": "^1.4.2",
"lie": "^3.0.1",
"lodash": "^4.0.0",
diff --git a/test/integration/hooks-test.js b/test/integration/hooks-test.js
index 6732e60..b9509cf 100644
--- a/test/integration/hooks-test.js
+++ b/test/integration/hooks-test.js
@@ -16,7 +16,7 @@ var options = {
test('sign in with pre & post hooks', function (t) {
store.clear()
- t.plan(2)
+ t.plan(5)
nock(baseURL)
.put('/session')
@@ -28,21 +28,20 @@ test('sign in with pre & post hooks', function (t) {
id: 'abc4567'
})
- account.on('pre:signin', function (options) {
- options.hooks.push(function () {
- return new Promise(function (resolve, reject) {
- setTimeout(function () {
- callOrder.push(1)
- resolve()
- }, 100)
- })
+ account.hook.before('signin', function (signInOptions) {
+ t.deepEqual(signInOptions, options, 'before signin hook receives options')
+ return new Promise(function (resolve) {
+ setTimeout(function () {
+ callOrder.push(1)
+ resolve()
+ }, 100)
})
})
- account.on('post:signin', function (options) {
- options.hooks.push(function () {
- callOrder.push(3)
- })
+ account.hook.after('signin', function (session, signInOptions) {
+ t.deepEqual(session, {id: 'abc4567', username: 'chicken@docs.com'}, 'after signin hook receives session')
+ t.deepEqual(signInOptions, options, 'after signin hook receives options')
+ callOrder.push(3)
})
account.on('signin', function () {
@@ -70,10 +69,8 @@ test('sign in with throw in pre hook', function (t) {
id: 'abc4567'
})
- account.on('pre:signin', function (options) {
- options.hooks.push(function () {
- throw new Error('signin aborted')
- })
+ account.hook.before('signin', function () {
+ throw new Error('signin aborted')
})
account.on('signin', function () {
@@ -102,10 +99,8 @@ test('sign in with throw in post hook', function (t) {
id: 'abc4567'
})
- account.on('post:signin', function (options) {
- options.hooks.push(function () {
- throw new Error('post:signin ooops')
- })
+ account.hook.after('signin', function (options) {
+ throw new Error('post:signin ooops')
})
account.on('signin', function () {
@@ -141,21 +136,17 @@ test('sign out with pre & post hooks', function (t) {
id: 'abc4567'
})
- account.on('pre:signout', function (options) {
- options.hooks.push(function () {
- return new Promise(function (resolve, reject) {
- setTimeout(function () {
- callOrder.push(1)
- resolve()
- }, 100)
- })
+ account.hook.before('signout', function () {
+ return new Promise(function (resolve, reject) {
+ setTimeout(function () {
+ callOrder.push(1)
+ resolve()
+ }, 100)
})
})
- account.on('post:signout', function (options) {
- options.hooks.push(function () {
- callOrder.push(3)
- })
+ account.hook.after('signout', function () {
+ callOrder.push(3)
})
account.on('signout', function () {
@@ -189,10 +180,8 @@ test('sign out with throw in pre hook', function (t) {
id: 'abc4567'
})
- account.on('pre:signout', function (options) {
- options.hooks.push(function () {
- throw new Error('signout aborted')
- })
+ account.hook.before('signout', function () {
+ throw new Error('signout aborted')
})
account.on('signout', function () {
@@ -227,10 +216,8 @@ test('sign out with throw in post hook', function (t) {
id: 'abc4567'
})
- account.on('post:signout', function (options) {
- options.hooks.push(function () {
- throw new Error('post:signout ooops')
- })
+ account.hook.after('signout', function () {
+ throw new Error('post:signout ooops')
})
account.on('signout', function () {
diff --git a/test/unit/sign-in-test.js b/test/unit/sign-in-test.js
index 1618383..adb123e 100644
--- a/test/unit/sign-in-test.js
+++ b/test/unit/sign-in-test.js
@@ -2,11 +2,16 @@ var simple = require('simple-mock')
var test = require('tape')
var signIn = require('../../lib/sign-in')
+var hookMock = simple.stub().callFn(function (name, options, callback) {
+ return callback(options)
+})
test('signIn without options', function (t) {
t.plan(1)
- signIn({})
+ signIn({
+ hook: hookMock
+ })
.then(t.fail.bind(t, 'must reject'))
.catch(t.pass.bind(t, 'rejects with error'))
})
@@ -14,7 +19,9 @@ test('signIn without options', function (t) {
test('signIn without password', function (t) {
t.plan(1)
- signIn({}, {
+ signIn({
+ hook: hookMock
+ }, {
username: 'username'
})
@@ -26,7 +33,9 @@ test('signIn without password', function (t) {
test('signIn without username', function (t) {
t.plan(1)
- signIn({}, {
+ signIn({
+ hook: hookMock
+ }, {
password: 'password'
})
@@ -39,6 +48,7 @@ test('successful account.signIn(options)', function (t) {
t.plan(6)
var state = {
+ hook: hookMock,
url: 'http://example.com',
cacheKey: 'cacheKey123',
emitter: {
@@ -97,7 +107,9 @@ test('signIn with request error', function (t) {
simple.mock(signIn.internals, 'request').rejectWith(new Error('Ooops'))
- signIn({})
+ signIn({
+ hook: hookMock
+ })
.then(t.fail.bind(t, 'must reject'))
@@ -113,6 +125,7 @@ test('signIn with same username', function (t) {
t.plan(2)
var state = {
+ hook: hookMock,
url: 'http://example.com',
cacheKey: 'cacheKey123',
emitter: {
@@ -141,8 +154,8 @@ test('signIn with same username', function (t) {
})
.then(function (accountProperties) {
- t.is(state.emitter.emit.callCount, 3, '3 Events emitted')
- t.is(state.emitter.emit.calls[1].arg, 'reauthenticate', 'Correct event emitted')
+ t.is(state.emitter.emit.callCount, 1, '1 Event emitted')
+ t.is(state.emitter.emit.calls[0].arg, 'reauthenticate', 'Correct event emitted')
simple.restore()
})
diff --git a/test/unit/sign-out-test.js b/test/unit/sign-out-test.js
index 79e1dc7..c8919cd 100644
--- a/test/unit/sign-out-test.js
+++ b/test/unit/sign-out-test.js
@@ -2,6 +2,7 @@ var simple = require('simple-mock')
var test = require('tape')
var signOut = require('../../lib/sign-out')
+var hookMock = simple.stub().callbackWith()
test('signOut()', function (t) {
t.plan(3)
@@ -13,6 +14,7 @@ test('signOut()', function (t) {
simple.mock(signOut.internals, 'clearSession').callFn(function () {})
var state = {
+ hook: hookMock,
url: 'http://example.com',
cacheKey: 'cacheKey123',
account: {
@@ -54,6 +56,7 @@ test('signOut() with request error', function (t) {
simple.mock(signOut.internals, 'request').rejectWith(new Error('Ooops'))
signOut({
+ hook: hookMock,
account: {
session: {}
},
@@ -73,6 +76,7 @@ test('signOut() without being signed in', function (t) {
t.plan(2)
signOut({
+ hook: hookMock,
account: {},
emitter: {
emit: simple.stub()
diff --git a/utils/get-state.js b/utils/get-state.js
index 7db7693..37b92fb 100644
--- a/utils/get-state.js
+++ b/utils/get-state.js
@@ -1,5 +1,6 @@
module.exports = getState
+var Hook = require('before-after-hook')
var EventEmitter = require('events').EventEmitter
var get = require('lodash/get')
@@ -26,9 +27,10 @@ function getState (options) {
})
var state = {
+ account: storedAccount,
cacheKey: cacheKey,
emitter: options.emitter || new EventEmitter(),
- account: storedAccount,
+ hook: new Hook(),
url: options.url,
validate: options.validate || function () {}
}