node-ottoman icon indicating copy to clipboard operation
node-ottoman copied to clipboard

updateById, updateMany, findOneAndUpdate does't support skipping validation and hooks

Open C0DE-IN opened this issue 3 years ago • 7 comments

Iam trying to update a property/ field of a document in couchbase using ottomanjs, while using updateById, updatemany, findOneAndUpdate getting a validation or hook error response , since iam trying to update only one field/property but ottoman expects all propeties of the document while validation and hooks execution and returns obivous error response.

is there a way to update a document in couchbase using ottomanjs only sending that paticular field/ property and skipping hooks and validations which was used implemented for document creation save().

The save() function triggers validate() hooks, because Ottoman has a built-in pre('save') hook that calls validate(). This means that all pre('validate') and post('validate') hooks get called before any pre('save') hooks. The updateById() function have the same behavior.

C0DE-IN avatar Aug 24 '22 08:08 C0DE-IN

Thanks for adding this here.

@gsi-alejandro can you assist ?>

AV25242 avatar Aug 25 '22 16:08 AV25242

hi @noiissyboy

can you provide a use case to reproduce this issue?

Note: This issue shouldn't happen, due to we recreate the entire object in memory before trying to update it, therefore all the fields will be present merging the existing one in the database + the ones you are trying to update.

I create this simple script to show how it works while updating partially a document using updateById method.

import { Ottoman, Schema } from "ottoman";

const main = async () => {
  const ottoman = new Ottoman();
  await ottoman.connect({
    connectionString: "couchbase://localhost",
    bucketName: "travel-sample",
    username: "Administrator",
    password: "password",
  });
  
  const userSchema = new Schema({
    name: { type: String, required: true },
    email: { type: String, required: true },
  });

  const User = ottoman.model("User", userSchema);

  await ottoman.start();

  const user = new User({ name: "Test", email: "[email protected]" });
  await user.save();

  console.log(user);

  const userUpdated = await User.updateById(user.id, { name: "Test Updated" });

  console.log(userUpdated);
};

main();

gsi-alejandro avatar Aug 29 '22 23:08 gsi-alejandro

hi @gsi-alejandro , @AV25242

I have created a nestjs project to replicate this issue, kindly check the link below https://github.com/noiissyboy/test-ottoman

C0DE-IN avatar Aug 30 '22 14:08 C0DE-IN

Hi @noiissyboy,

Is this the error message you are facing? image

Maybe you need to update the logic in the hook or change it from 'validate' to 'save':

  1. Create logic to validate unique emails on pre save (length > 0) and update (length > 1 (itself)) hooks
  2. Just validate when saving, only need to change pre validate to save.

gsi-alejandro avatar Aug 30 '22 18:08 gsi-alejandro

Hi @gsi-alejandro @AV25242 Thanks for your suggestion changing from 'validate' to 'save' helps skipping 'validate' hooks but I am getting ValidationError from schema validator for password field.

exception

I have updated my app by adding bcrypt to crypt password, kindly check once.

Thanks Vignesh

C0DE-IN avatar Aug 31 '22 13:08 C0DE-IN

hi @noiissyboy

For this case you should create a validation function to use in the save hook, due to the string you're validating will only exist while creating; the stored value is a hash value from it. Therefore it can be registered as a validator because it should be triggered for the original value before saving it.

From this point you can create a function:

const passwordValidator = (value: string) => {
  const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,60}$/
  if (value && !value.match(regex)) {
    throw new BadRequestException('password doesn\'t satisfies requirement!');
  }
  return true;
}

And use it in the save hook like this:

preHooks: {
      'save': async (doc:any) => {
        const UserModel = getModel('User');
       
        const user= await UserModel.findOneByEmail(doc.email)
       
        if(user.rows.length >0){
          throw new ConflictException(doc.email + 'user already exist!');
        }
        if (passwordValidator(doc.password)) {
          const temp = await hash(doc.password, 12);
          doc.password = temp;
        }
      }
    }

or just:

preHooks: {
      'save': async (doc:any) => {
        const UserModel = getModel('User');
       
        const user= await UserModel.findOneByEmail(doc.email)
       
        if(user.rows.length >0){
          throw new ConflictException(doc.email + 'user already exist!');
        }

       passwordValidator(doc.password);  // This one don't require the to return true
       const temp = await hash(doc.password, 12);
       doc.password = temp;
    }

gsi-alejandro avatar Aug 31 '22 19:08 gsi-alejandro

Hi @gsi-alejandro ,

Thanks that worked, kindly consider disabling validation checks for all fields while updating partial fields in ottoman by default. since Schema validator and validate hooks are unused and update document is major action done in database.

C0DE-IN avatar Sep 01 '22 07:09 C0DE-IN

I am closing this issue as no action is needed particularly for this issue

AV25242 avatar Oct 10 '22 20:10 AV25242