-
-
Save KidkArolis/98c5305dfe3c0f259d34cc1061eeb522 to your computer and use it in GitHub Desktop.
/* | |
Install this hook in app.hooks.js | |
module.exports = app => { | |
app.hooks({ | |
before: { | |
all: [ | |
addParamsForward() | |
] | |
} | |
}) | |
} | |
Then use whenever you want to forward params, e.g. in a hook: | |
const { app, params } = context | |
app.service('another-service').find({ query: { id: 'x' }, ...params.forward() }) | |
app.service('another-service').get(id, params.forward()) | |
app.service('another-service').patch(id, {smth: 'x' }, params.forward()) | |
*/ | |
const { has } = require('lodash') | |
const paramsSeen = new WeakSet() | |
module.exports = function addParamsForward() { | |
return context => { | |
if (paramsSeen.has(context.params)) { | |
throw new Error( | |
`Params should never be shared across services, always use params.forward(). Fix calls to ${context.path}#${context.method}`, | |
) | |
} | |
paramsSeen.add(context.params) | |
context.params.forward = ({ only, omit = [] } = {}) => { | |
const { params } = context | |
const forwardedParams = { | |
caller: { | |
path: context.path, | |
method: context.method, | |
type: context.type, | |
}, | |
requestId: context.params.requestId, | |
forwarded: true, | |
} | |
;( | |
only || [ | |
'authenticated', | |
'user', | |
'transaction', | |
'skipEvents', | |
'permissions', | |
] | |
).forEach(param => { | |
if (has(params, param) && !omit.includes(param)) { | |
forwardedParams[param] = params[param] | |
} | |
}) | |
if (forwardedParams.authenticated) { | |
forwardedParams.authentication = params.authentication | |
} | |
return forwardedParams | |
} | |
} | |
} |
No! In case you want to say only forward transaction in some internal call, e.g. params.forward({ only: ['transaction'] })
, then that means you explicitly did not want to attached .authentication.
Hi @KidkArolis,
I have tried to use the hook, but met with some strange error.
I'm using typescript
, and the example is using Post
and Comment
. I added your hook into app.hook.before.all
and I have a before
hook in Post
service that creates comments
.
const createComment = async (context: HookContext) => {
const { data, app, params } = context;
// logged out with no issue
console.log(params.forward());
// const forward = { ...params.forward() };
(app as Application)
.service('comments')
.create(
{ text: data.comments[0]},
// if I pass as such, it will throw TypeError: Found non-callable @@iterator
...params.forward()
);
delete context.data.comments;
context.data.createdBy = params.user?.email;
return context;
};
So I switched to this
// so I set it here, and use it below
const forward = { ...params.forward() };
(app as Application)
.service('comments')
.create(
{ text: data.comments[0]},
forward
);
It works just fine, in the comments
service, I can grab the params
being passed down from Post
. However, this works if this is a single call only. If I were do this...
const forward = { ...params.forward() };
const commentsPromise = data.comments?.map(async (comment: string) => {
(app as Application)
.service('comments')
.create({
text: comment,
}, forward);
});
if (commentsPromise) {
await Promise.all(commentsPromise);
}
This only works for the first call, subsequent call will throw the following error
Error: Params should never be shared across services, always use params.forward(). Fix calls to comments#create
Any idea on the two issue I face? Appreciate your help
@bwgjoseph, this is a convention I enforce in my Feathers applications - to never share the same params object between different service calls. This is because params object might get mutated in hooks affecting other services unexpectedly.
In your case, simple to fix, call params.forward() for each call, e.g.:
const commentsPromise = data.comments?.map(async (comment: string) => {
(app as Application)
.service('comments')
.create({
text: comment,
}, params.forward());
});
if (commentsPromise) {
await Promise.all(commentsPromise);
}
ah! it works, but still can't figure out why using spread ...params.forward()
will trigger the non-callable @@iterator
error. Other than that, it looks good now.
If you know what's causing the error, do let me know.
Thanks!
Because you can only spread properties in an object:
let a = { x: 'y' }
let b = { ...a }
Or you can spread arrays as arguments:
let a = ['x', 'y', 'z']
example(...a)
You can't spread an object as arguments:
// not valid!!
// throws non-callable @@iterator / is not iterable
let a = { x: 'y' }
example(...a)
In Feathers, params
is an object, params.forward() is also an object, so:
// invalid
service.get(1, ...params)
service.get(1, ...params.forward())
// valid
service.get(1, params)
service.get(1, params.forward())
service.get(1, { ...params })
service.get(1, { ...params.forward() })
// invalid
service.find(...params)
service.find(...params.forward())
// valid
service.find({ query: { email: 'x', }, ...params })
service.find({ query: { email: 'x', }, ...params.forward() })
That's it.
Thank you so much.
Hello @KidkArolis,
I noticed that in the forwardedParams.caller.type
is always before
, is that intended? I would imagine that it should be whoever called the forward()
?
// I have a hook x at the after-remove hook of service y
// now I call to remove
app.service('y').remove(id)
// within hook x, it calls service z (note hook x is in after-remove hook of service y)
app.service('z').remove(anotherId, params.forward());
So when I logs out the forwardedParams
before the return
in addParamsForward()
, the caller.type
is before
.
@bwgjoseph you're right, it could be fixed by readding addParamsForward() hook in after all, so that it uses updated the closed over context when computing forwardedParams.
Hi, should line 62 be
params.authenticated
instead?