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

Missing support for "additionalProperties" #55

Closed
chkp-michaelo opened this issue Nov 25, 2019 · 5 comments
Closed

Missing support for "additionalProperties" #55

chkp-michaelo opened this issue Nov 25, 2019 · 5 comments

Comments

@chkp-michaelo
Copy link

Missing support for "additionalProperties":
https://json-schema.org/understanding-json-schema/reference/object.html

Specific use case:
Forbid unexpected input with "additionalProperties":false.

@kristianmandrup
Copy link
Owner

kristianmandrup commented Dec 3, 2019

Never heard about or used this JSON schema feature. Please write a failing spec (test) with the setup requested and the expected result and make a PR. You can also use the existing infra to add your own logic to override or extend the default.

For example, one can allow additional properties, but only if they are each a string:

{
  "type": "object",
  "properties": {
    "number":      { "type": "number" },
    "street_name": { "type": "string" },
    "street_type": { "type": "string",
                     "enum": ["Street", "Avenue", "Boulevard"]
                   }
  },
  "additionalProperties": { "type": "string" }
}

How would this example ideally be reflected using Yup? We could down reverse engineer from there...

@kristianmandrup
Copy link
Owner

I've added an empty test for additionalProperties. Please have a go at it ;)
You could extend YupBuilder

class MyYupBuilder extends YupBuilder {
  constructor(schema, config = {}) {
    super(schema, config);
    this.additionalProps = schema.additionalProperties
  }

  propsToShape(opts) {
    const shape = super.propsToShape(opts)
    const { additionalProps } = this
    if (additionalProps) {
      // modify yup shape
    }
    return shape
  }
}

Figure out what yup shape would work in this case, then use the recipe above :) Would love to see a PR for this.

BTW: Just fixed the library core so it should work again. The new ConstraintBuilder had messed the internals up a bit and was more difficult to get right than anticipated. Sorry about that.

@kristianmandrup
Copy link
Owner

In the latest commit I've made a slight change to the YupBuilder to make this easier (more flexible infra)

  constructor(...) {
    this.additionalProps = this.getAdditionalProperties(schema);
  }

  propsToShape(opts = {}) {
    const shape = this.objPropsToShape(opts);
    this.objPropsShape = shape;
    this.addPropsShape = this.additionalPropsToShape(opts, shape);
    return shape;
  }

  additionalPropsToShape(opts, shape) {
    // do your magic here using this.additionalProps 
   // make new yup constraint function calls on the incoming yup shape object
    return shape;
  }

@kristianmandrup
Copy link
Owner

kristianmandrup commented Dec 4, 2019

I think you could use the yup test method to add this extra functionality

let jimmySchema = string().test(
  'is-jimmy',
  '${path} is not Jimmy',
  value => value === 'jimmy',
);

The test function would then need a scope with access to the property keys traversed/used as part of the normal validation to check if this property is an additional property not otherwise covered in this context.

const propNamesUsed = Object.keys(this.properties)
const additionalPropertyTestFn = (name, errorFn, (value) => {
  if (propNamesUsed.includes(name)) return true; // valid prop if already used
  // test logic here

  if (value.type == 'string') {
    const schema = toYupString(schemaValue)
    schema.validateSync(
 )   

  return true
})
shape.test = additionalPropertyTestFn

Perhaps better to use this variant of test

const propNamesUsed = Object.keys(this.properties)
const schemaValue = this.additionalProperties
const additionalPropertyTestFn = (opts) => {
  if (propNamesUsed.includes(name)) return true; // valid prop if already used

  const args = createSchemaOpts(opts, schemaValue)

  // test logic here (ideally generalise to support all supported types)
  if (value.type == 'string') {
    const schema = toYupString(...args)
    return validate(schema, value)
 )   

  return true
})
shape.test = additionalPropertyTestFn

@kristianmandrup
Copy link
Owner

I've now added a note on this to the Readme of the library

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

No branches or pull requests

2 participants