feathersjs-ecosystem / feathers-hooks-common

Useful hooks for use with FeathersJS services.
https://hooks-common.feathersjs.com
MIT License
193 stars 89 forks source link

stashBefore - add support for bulk operations #757

Open muhammadyusuf-kurbonov opened 3 months ago

muhammadyusuf-kurbonov commented 3 months ago

Steps to reproduce

  1. Create a Mongoose service with option multi: true.
  2. Add before patch hook: stashBefore
  3. Run service.patch(null, { query: { date: "2024-08-14" }, mongoose: { upsert: true } }

(First please check that this issue is not already solved as described here)

Expected behavior

At least, I don't want an error: Not found element with id null. At best, I want to stash these multiple item.

Actual behavior

My log is flooded with errors: Not found element with id null, because our app make some bulk writes to this table.

Module versions (especially the part that's not working): Feathers v3 (yes it is old, but project is legacy and huge) Feathers mongoose: 7.3.1 (tried also with 8) Feathers-hooks-common: 4.20.2 (according source code, this error will happen in latest version)

NodeJS version: NodeJS 16

Operating System: Linux, Fedora 40

muhammadyusuf-kurbonov commented 3 months ago

Sorry, that have opened issue with legacy project setup, but I'm sure that this error will happen also in latest Feathers versions too

muhammadyusuf-kurbonov commented 3 months ago

Now I have copied hook source code and modified myself:

export default function (prop): Hook {
  const beforeField = prop || 'before';

  return context => {
    checkContext(context, 'before', ['get', 'update', 'patch', 'remove'], 'stashBefore');

    if (context.params.query && context.params.query.$disableStashBefore) {
      delete context.params.query.$disableStashBefore;
      return context;
    }

    if ((context.id === null || context.id === undefined) && !context.params.query) {
      throw new errors.BadRequest('Id is required. (stashBefore)');
    }

    const params = context.method === 'get' ? context.params : {
      provider: context.params.provider,
      authenticated: context.params.authenticated,
      user: context.params.user
    };

    params.query = params.query || {};
    params.query.$disableStashBefore = true;

    const bulkOperation = context.service.options.multi && context.id === null;

    const data = bulkOperation ? context.service.find(params) : context.service.get(context.id, params);

    return data.then(data => {
      delete params.query.$disableStashBefore;

      context.params[beforeField] = JSON.parse(JSON.stringify(data));
      return context;
    }).catch(() => context);
  };
}