-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ts: Add setField hook from feathers-authentication-hooks (#664)
* feat(set-field): new hook 'set-field' * chore: use assert instead of assert/strict * docs: copy 'setField' docs from feathers-authentication-hooks * docs: add notable changes section on overview * docs: add jsdoc for setField
- Loading branch information
1 parent
5898a56
commit d59b6b5
Showing
7 changed files
with
284 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import _get from 'lodash/get'; | ||
import _setWith from 'lodash/setWith'; | ||
import _clone from 'lodash/clone'; | ||
import _debug from 'debug'; | ||
import { checkContext } from '../utils/check-context'; | ||
import { Forbidden } from '@feathersjs/errors'; | ||
import type { Hook } from '@feathersjs/feathers'; | ||
import type { SetFieldOptions } from '../types'; | ||
|
||
const debug = _debug('feathers-hooks-common/setField'); | ||
|
||
/** | ||
* The `setField` hook allows to set a field on the hook context based on the value of another field on the hook context. | ||
* {@link https://hooks-common.feathersjs.com/hooks.html#setfield} | ||
*/ | ||
export function setField ( | ||
{ as, from, allowUndefined = false }: SetFieldOptions | ||
): Hook { | ||
if (!as || !from) { | ||
throw new Error('\'as\' and \'from\' options have to be set'); | ||
} | ||
|
||
return context => { | ||
const { params, app } = context; | ||
|
||
if (app.version < '4.0.0') { | ||
throw new Error('The \'setField\' hook only works with Feathers 4 and the latest database adapters'); | ||
} | ||
|
||
checkContext(context, 'before', null, 'setField'); | ||
|
||
const value = _get(context, from); | ||
|
||
if (value === undefined) { | ||
if (!params.provider || allowUndefined) { | ||
debug(`Skipping call with value ${from} not set`); | ||
return context; | ||
} | ||
|
||
throw new Forbidden(`Expected field ${as} not available`); | ||
} | ||
|
||
debug(`Setting value '${value}' from '${from}' as '${as}'`); | ||
|
||
return _setWith(context, as, value, _clone); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import assert from 'assert'; | ||
import feathers from '@feathersjs/feathers'; | ||
import memory from 'feathers-memory'; | ||
import { setField } from '../../src'; | ||
|
||
import type { Application } from '@feathersjs/feathers'; | ||
|
||
describe('setField', () => { | ||
const user = { | ||
id: 1, | ||
name: 'David' | ||
}; | ||
|
||
let app: Application; | ||
|
||
beforeEach(async () => { | ||
app = feathers(); | ||
app.use('/messages', memory()); | ||
app.service('messages').hooks({ | ||
before: { | ||
all: [setField({ | ||
from: 'params.user.id', | ||
as: 'params.query.userId' | ||
})] | ||
} | ||
}); | ||
await app.service('messages').create({ | ||
id: 1, | ||
text: 'Message 1', | ||
userId: 1 | ||
}); | ||
await app.service('messages').create({ | ||
id: 2, | ||
text: 'Message 2', | ||
userId: 2 | ||
}); | ||
}); | ||
|
||
it('errors when options not set', () => { | ||
assert.throws(() => app.service('messages').hooks({ | ||
before: { | ||
// @ts-expect-error | ||
get: setField() | ||
} | ||
})); | ||
assert.throws(() => app.service('messages').hooks({ | ||
before: { | ||
// @ts-expect-error | ||
get: setField({ as: 'me' }) | ||
} | ||
})); | ||
assert.throws(() => app.service('messages').hooks({ | ||
before: { | ||
// @ts-expect-error | ||
get: setField({ from: 'you' }) | ||
} | ||
})); | ||
}); | ||
|
||
it('errors when used with wrong app version', async () => { | ||
app.version = '3.2.1'; | ||
|
||
await assert.rejects(async () => { | ||
await app.service('messages').get('testing'); | ||
}, { | ||
message: 'The \'setField\' hook only works with Feathers 4 and the latest database adapters' | ||
}); | ||
}); | ||
|
||
it('find queries with user information, does not modify original objects', async () => { | ||
const query = {}; | ||
const results = await app.service('messages').find({ query, user }); | ||
|
||
assert.equal(results.length, 1); | ||
assert.deepEqual(query, {}); | ||
}); | ||
|
||
it('adds user information to get, throws NotFound event if record exists', async () => { | ||
await assert.rejects(async () => { | ||
await app.service('messages').get(2, { user }); | ||
}, { | ||
name: 'NotFound', | ||
message: 'No record found for id \'2\'' | ||
}); | ||
|
||
const result = await app.service('messages').get(1, { user }); | ||
|
||
assert.deepEqual(result, { | ||
id: 1, | ||
text: 'Message 1', | ||
userId: 1 | ||
}); | ||
}); | ||
|
||
it('does nothing on internal calls if value does not exists', async () => { | ||
const results = await app.service('messages').find(); | ||
|
||
assert.equal(results.length, 2); | ||
}); | ||
|
||
it('errors on external calls if value does not exists', async () => { | ||
await assert.rejects(async () => { | ||
await app.service('messages').find({ | ||
provider: 'rest' | ||
}); | ||
}, { | ||
name: 'Forbidden', | ||
message: 'Expected field params.query.userId not available' | ||
}); | ||
}); | ||
|
||
it('errors when not used as a before hook', async () => { | ||
app.service('messages').hooks({ | ||
after: { | ||
get: setField({ | ||
from: 'params.user.id', | ||
as: 'params.query.userId' | ||
}) | ||
} | ||
}); | ||
|
||
await assert.rejects(async () => { | ||
await app.service('messages').get(1); | ||
}, { | ||
message: 'The \'setField\' hook can only be used as a \'before\' hook.' | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters