z0mt3c / hapi-swaggered

Yet another hapi plugin providing swagger compliant API specifications based on routes and joi schemas to be used with swagger-ui.
MIT License
82 stars 38 forks source link

missing joi.alternatives fields in model #30

Open Gregy opened 9 years ago

Gregy commented 9 years ago

I have a fairly complex validation object shared on multiple routes. Sometimes with slight modifications which are handled by joi.alternatives:

//generic.id = Joi.alternatives().try(
//  Joi.string().length(24).alphanum().lowercase(),
//  Joi.object().type(mongoose.Types.ObjectId)
//);
exports.request = Joi.object().keys({
  name: Joi.string().required().min(3).max(50).regex(/^[a-z0-9][a-z0-9-]+$/),
  state: exports.state.state,
  //only require hgroup on create
  hgroup: generic.id.when('$isCreate', { is: Joi.any().only(true).required(), then: Joi.required() }),
  cdrom: Joi.alternatives().when('cdpath', {is: Joi.any().only(undefined), then:generic.id, otherwise: Joi.any().forbidden()}),
  cdpath: Joi.string().regex(/^[^\/\s](?:(?!\/\.\.\/).)+\.iso$/i), //match any string ending with .iso not containing /../ and not beginning with /
  ram: Joi.number().integer().required(),
  cpuno: Joi.number().integer().required(),
  hdds: Joi.array().required().items(Joi.object().rename('id', '_id').keys({
    _id: Joi.alternatives().when('$isCreate', {
      is: Joi.any().only(true).required(),
      then: Joi.any().forbidden(),
      otherwise: generic.id
    }),
    size: Joi.number().integer().required(),
    description: Joi.string().max(300),
    template: generic.id,
    storage: Joi.string().required()
  })),
  nics: Joi.array().required().items(Joi.object().rename('id', '_id').keys({
    _id: Joi.alternatives().when('$isCreate', {
      is: Joi.any().only(true).required(),
      then: Joi.any().forbidden(),
      otherwise: generic.id
    }),
    ip: Joi.string().required().lowercase().regex(/^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/),
    subnet: Joi.string().required().lowercase().regex(/^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/),
    gateway: Joi.string().lowercase().regex(/^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/),
    vlan: Joi.number().integer().required(),
    description: Joi.string().max(300),
  })),
}).meta({className:'VMRequest'});

Alternatives fields seems to be ignored completely in generated model:

VMRequest {
name (string),
state (string) = ['stopped' or 'running' or 'restarting' or 'destroying'],
cdpath (string, optional),
ram (integer),
cpuno (integer),
hdds (Array[_idSizeDescriptionTemplateStorageModel]),
nics (Array[_idIpSubnetGatewayVlanDescriptionModel])
}
_idSizeDescriptionTemplateStorageModel {
size (integer),
description (string, optional),
storage (string)
}
_idIpSubnetGatewayVlanDescriptionModel {
ip (string),
subnet (string),
gateway (string, optional),
vlan (integer),
description (string, optional)
}
z0mt3c commented 9 years ago

Yes - joi.alternatives() are currently not supported and will be stripped. I would be interested how your schema should look in swagger 2.0 specs (designed by hand) and then we should have a look how close we can get.

mwawrusch commented 9 years ago

I have a similar problem. The following model actually breaks swagger (with a 500 error):

kindSeparator = Joi.object({
  pagingKind: Joi.string().required()["default"]("separator").example("separator").description("Specifies the kind of page.").valid('separator')
}).description("Instruction to render a page separator").meta({
  className: 'PaginationPageSeparator'
}).options({
  allowUnknown: true,
  stripUnknown: true
});

kindPage = Joi.object({
  pagingKind: Joi.string().required()["default"]("page").example("page").description("Specifies the kind of page.").valid(['page']),
  pageNumber: Joi.number().integer()["default"](0).example(0).description("The zero based page number of this page.").required(),
  pageNumberDisplay: Joi.string()["default"]("1").example("1").description("The one based page number as a string, useful for displaying to the end user.").required(),
  nextUrl: me.urlOptional("The request url to retrieve this page, or null."),
  active: Joi.boolean()["default"](false).example(true).description("Boolean that indicates that this item is the currently active page.").required()
}).description("The description of a single page").meta({
  className: 'PaginationPagePage'
}).options({
  allowUnknown: true,
  stripUnknown: true
});

return Joi.alternatives(kindPage, kindSeparator).description("The description of a single page").meta({
  className: 'PaginationPage'
}).options({
  allowUnknown: true,
  stripUnknown: true
});

THe idea is to have an alternative between object a and b, where a has a specific kind and b another.

eawer commented 7 years ago

@z0mt3c Hello, any updates on this since then? Is it still not working?

doriansmiley commented 7 years ago

Any updates on this?

z0mt3c commented 7 years ago

Edit: mixed up issues

Alternatives have no special implementation yet - but at least it shouldn't throw... will double check with the new version 2.10.0.

As i mentioned earlier: joi.alternatives() are currently not supported and will be stripped. I would be interested how your schema should look in swagger 2.0 specs (designed by hand) and then we should have a look how close we can get.

I know what alternatives do but when i created this module the swagger specs where not capable of polymorphism - at least i didn't find a way. Can u provide me a swagger example schema with polymorphism?

KostiantynKopytov commented 6 years ago

Should be partially possible with Swagger 3.0: https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/

kPOWz commented 6 years ago

Discriminator Object adds support for polymorphism - https://swagger.io/specification/#discriminatorObject