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

Hooks #125

Merged
merged 4 commits into from
Dec 22, 2016
Merged

Hooks #125

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
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ account.on('signout', redirectToHome)
- [account.one](#accountone)
- [account.off](#accountoff)
- [Events](#events)
- [Hooks](#hooks)
- [Requests](#requests)

### Constructor
Expand Down Expand Up @@ -994,6 +995,42 @@ hoodie.off('connectionstatus:disconnected', showNotification)
</tr>
</table>

### 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()
})
```

<table>
<thead>
<tr>
<th align="left">
Hook
</th>
<th align="left">
Arguments
</th>
</tr>
</thead>
<tr>
<th align="left"><code>signin</code></th>
<td><code>options</code> as they were passed into <code>account.signIn(options)</code></td>
</tr>
<tr>
<th align="left"><code>signout</code></th>
<td><code>{}</code></td>
</tr>
</table>

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,
Expand Down
7 changes: 5 additions & 2 deletions admin/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module.exports = AccountAdmin

var EventEmitter = require('events').EventEmitter
var Hook = require('before-after-hook')

var getAccount = require('../utils/get-account')

Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
86 changes: 29 additions & 57 deletions lib/sign-in.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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
})
})
}
41 changes: 8 additions & 33 deletions lib/sign-out.js
Original file line number Diff line number Diff line change
@@ -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')
Expand All @@ -16,49 +15,25 @@ 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',
headers: {
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)
})
})
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
69 changes: 28 additions & 41 deletions test/integration/hooks-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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: '[email protected]'}, 'after signin hook receives session')
t.deepEqual(signInOptions, options, 'after signin hook receives options')
callOrder.push(3)
})

account.on('signin', function () {
Expand Down Expand Up @@ -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 () {
Expand Down Expand Up @@ -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 () {
Expand Down Expand Up @@ -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 () {
Expand Down Expand Up @@ -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 () {
Expand Down Expand Up @@ -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 () {
Expand Down
Loading