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

Question: how to extend joi to add some key by rule? #1201

Closed
XGHeaven opened this issue May 26, 2017 · 12 comments
Closed

Question: how to extend joi to add some key by rule? #1201

XGHeaven opened this issue May 26, 2017 · 12 comments
Assignees
Labels
feature New functionality or improvement
Milestone

Comments

@XGHeaven
Copy link
Contributor

Context

  • node version: 7.10.0
  • joi version: 10.3.1
  • environment (node, browser): node
  • used with (hapi, standalone, ...): standalone
  • any other relevant information:

Question

I use a schema in many place, such as

joi.object().keys({
    user: joi.string().required(),
    pwd: joi.string().required(),
})

but i don't like export it as a variable then import it to use. i want a easy way.

joi.object().userinfo()

I don't know how to implement by joi.extend(). But I use follow code:

joi.object().constructor.prototype['userinfo'] = function(){
    return this.concat({
        user: joi.string().required(),
        pwd: joi.string().required(),
    })
}

so anyone knows a elegance way??

@WesTyler
Copy link
Contributor

If you don't want to export the base schema and compose your subschema from it, I would definitely use Joi's extend instead of modifying the prototype of Joi.object.

Check out #1179 for a more detailed discussion of a similar extension.

That being said, since you aren't adding new rules or data coercion or anything, composing with Node honestly will be the easiest...

// base.js
const Joi = require('joi');

module.exports = Joi.object().keys({
    user: joi.string().required(),
    pwd: joi.string().required()
});
// any other file
const BaseSchema = require('./base.js');

const extendedSchema = BaseSchema.keys({ /*additional keys here*/ });
/*
as opposed to using the userInfo extension:
const extendedSchema = Joi.object().keys({ /*additional keys here*/ }).userInfo();
/*

@XGHeaven
Copy link
Contributor Author

@WesTyler
Thanks, I want to know how to write extension without modify prototype.
I try to this, but no effect. :(

joi.extend({
    name: 'object',
    base: joi.object(),
    rules: [{
        name: 'userinfo',
        setup(param) {
            this.concat({
                user: joi.string().required(),
                pwd: joi.string().required(),
            })
    }]
})

@Marsup Marsup self-assigned this Jun 3, 2017
@Marsup Marsup added the support Questions, discussions, and general support label Jun 3, 2017
@Marsup
Copy link
Collaborator

Marsup commented Jun 3, 2017

Looks to me like you just want :

Joi.extend({
    name: 'userinfo',
    base: Joi.object().keys({
        user: joi.string().required(),
        pwd: joi.string().required(),
    })
})

@XGHeaven
Copy link
Contributor Author

XGHeaven commented Jun 4, 2017

@Marsup
you can only write joi.userinfo() as you write, not joi.object().userinfo() that i want to

@DavidTPate
Copy link
Contributor

@XGHeaven I don't understand why it is necessary to write Joi.object().userinfo(). In this case what's happening is that Joi is being extended with a type called userinfo which is itself an object and has the following schema:

Joi.object().keys({
        user: joi.string().required(),
        pwd: joi.string().required(),
    })

@XGHeaven
Copy link
Contributor Author

XGHeaven commented Jun 6, 2017

@DavidTPate I think userinfo is a decorator for object, like min,max for object.
userinfo decorate a object with user and pwd keys and user is joi.string().required(), pwd is joi.string().required()
So I prefer joi.object().userinfo() not joi.userinfo()...

@DavidTPate
Copy link
Contributor

Gotcha, you can find some additional examples of Joi.extend() here.

@DavidTPate DavidTPate added the non issue Issue is not a problem or requires changes label Jun 6, 2017
@Marsup
Copy link
Collaborator

Marsup commented Jun 6, 2017

It doesn't seem like a very hard one to support, but I don't think it's possible right now. I'll keep this around as a request for when I have time to do it, or anyone feel free to try. It's a small modification of the setup call to use an eventually returned schema value.

@Marsup Marsup reopened this Jun 6, 2017
@Marsup Marsup added request and removed non issue Issue is not a problem or requires changes support Questions, discussions, and general support labels Jun 6, 2017
@XGHeaven
Copy link
Contributor Author

XGHeaven commented Jun 7, 2017

I suggest that:

  • if setup return undefined, nothing to do, just like before.
  • if setup return Joi object, to replace schema.
  • if setup return others, throw a error with 'Joi.extend() rule with setup must be return undefined, or Joi object' message.

I'm trying to do this.

Marsup added a commit that referenced this issue Jun 14, 2017
#1201 - setup can return joi object to replace origin schema
@Marsup Marsup added this to the 10.6.0 milestone Jun 14, 2017
@hueniverse hueniverse added feature New functionality or improvement and removed request labels Sep 19, 2019
@katlimruiz
Copy link

katlimruiz commented Feb 10, 2020

My solution has been:

// my common type
const createAudit = Joi.object().keys({
    createdBy: Joi.string()
        .required(),
    createdOn: Joi.date().iso()
        .required()
})
    .and('createdBy', 'createdOn');
// my final type
const schemaFinal = Joi.object({
    id: Joi.string()
        .required(),
    //...
})
    .concat(createAudit)

So now the object as

var o = {
  id: '123',
  createdBy: 'creator'
};

await schemaFinal.validateAsync(o);

would give error where createdOn is required and createdBy and createdOn need to be paired.

@aggarwaldev
Copy link

Hey @XGHeaven,

How did you get this working with Joi 17.1.1

I am struggling with a similar situation and thought you would be kind enough to help me with this. :)

@rajeshwarpatlolla
Copy link

rajeshwarpatlolla commented Aug 24, 2020

@katlimruiz your solutions works for me to some extent, where i can extend keys. I also need to add few more keys in the final schema with out modifying base schema.

My base schema is

const ItemSchema = Joi.object({
  name: Joi.string().required(),
  price: Joi.number().required().min(0),
  description: Joi.string().optional(),
  variants: Joi.array().items(Joi.object({ name: Joi.string().required(), price: Joi.number().required() }),
})

And my final schema would be

const OrderSchema = Joi.object({
  cid: Joi.string(),
  totalQuantity: Joi.number().min(0),
  itemTotal: Joi.number().min(0),
  items: Joi.array().items(
    Joi.object({
      quantity: Joi.number().required(),
    }).concat(ItemSchema)
  })
};

In this case my final schema would have keys cid, totalQuantity, itemTotal, items array. This items array will have objects matching to ItemSchema, something like

{
  "cid": "string",
  "totalQuantity": 0,
  "itemTotal": 0,
  "items": [
    {
      "name": "string",
      "price": 0,
      "description": "string",
      "variants": [
        {
          "name": "string",
          "price": 0
        }
      ]
    }
  ]
}

My actual requirement is also to add few more fields like quantity in variants object.

{
  "cid": "string",
  "totalQuantity": 0,
  "itemTotal": 0,
  "items": [
    {
      "name": "string",
      "price": 0,
      "description": "string",
      "variants": [
        {
          "name": "string",
          "price": 0,
          "quantity": 0
        }
      ]
    }
  ]
}

So how can we add few more fields inside the nested array in the ItemSchema i.e. base schema. The ItemSchema should be intact and the additional fields should be part of OrderSchema only.

Please let me know the solution if anyone has idea on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New functionality or improvement
Projects
None yet
Development

No branches or pull requests

8 participants