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

$data support for custom keywords #146

Closed
clouddra opened this issue Mar 21, 2016 · 9 comments
Closed

$data support for custom keywords #146

clouddra opened this issue Mar 21, 2016 · 9 comments
Labels

Comments

@clouddra
Copy link

Is it possible to use/resolve $data for custom validation scenarios?

@epoberezkin
Copy link
Member

Your custom keyword is just passed the schema. So you can interpret $data in the function you supply. If your custom keyword is a macro it will just work of course, but if it is any other kind you will have to write code to process any schema it is passed, including $data keyword.

@clouddra
Copy link
Author

@epoberezkin Thanks for the clarification! Will close this issue.

@epoberezkin
Copy link
Member

I think a custom keyword could have $data: true in its definition that would allow its value to be $data reference without the need to write code for it - it would be resolved by ajv.

@EricMCornelius
Copy link

I'm actually trying to figure out something similar right now - I want to use json-pointer (non-relative) references inside my data documents, and resolve/enforce those refs as part of validation.

It's unclear to me what the best approach is to handle this. Should I piggyback on existing functionality by treating this as a meta-schema?

@epoberezkin
Copy link
Member

I have several interpretations of what you may want to do, could you please provide some details or examples maybe? Like what the schema and data may look like.

@EricMCornelius
Copy link

Essentially I just want to be able to resolve json-pointer references in my documents which are being validated:

Sample document:

examples:
  first:
    field1: test
    field2: test2
  second:
    field1: test3
    field2: test4
routes:
  example:
    $ref: '#/examples/first'

Sample schema:

type: object
properties:
  routes:
    type: object
    properties:
      example:
        $ref: '#/definitions/example'
definitions:
  example:
    type: object
    properties:
      field1:
        type: string
      field2:
        type: string

What I need here is to resolve the $ref at validation time, and check the resolved value against the schema expected at this node. It would be easily doable if you provide a way to extend your type coercion rules.

@EricMCornelius
Copy link

I am currently pre-processing my input document and replacing the $ref keys with corresponding self-references as a workaround - but this makes generating inadvertent cyclic references a concern.

@epoberezkin
Copy link
Member

I understand now. Extending type coercion rules wasn't in my plan - I will think about it.

But even if it were available, using it for this particular problem seems a bit hacky to me.

I don't know whether you have seen what you could do with custom keywords but they seem to be the correct solution.

I don't think replacing $ref with references (or with deep clones) is a bad idea, and it can be done with the schema itself so you don't have to iterate data in your code:

{
  "allOf": [
    {
      "id": "#replaceJsonPointers",
      "description": "this subschema replacing all $refs recursively using custom keyword jsonPointerRef",
      "properties": {
        "$ref": { "allOf": [
          { "type": "string", "format": "json-pointer" },
          { "jsonPointerRef": true }
        ] },
        "additionalProperties": { "$ref": "#replaceJsonPointers" },
        "items": { "$ref": "#replaceJsonPointers" }
      }
    },
    { "description": "your validation schema here" }
  ]
}

And then the keyword itself can be defined in this way:

var jsonPointer = require('json-pointer');
var ajv = Ajv({ passContext: true });

ajv.addKeyword('jsonPointerRef', {
  type: 'string',
  schema: false,
  validate: function(data, dataPath, parentData, propertyName) {
    // some error handling/validation is needed here that the replacing object doesn't contains $refs e.g.
    var ref = data;
    var dataRef = jsonPointer.get(this.data, ref);
    parentData[propertyName] = dataRef; // that would replace the value of $ref with reference
    // if you want to replace { $ref: '...' } with reference you may use jsonPointer to do it.
  }
});

var validate = ajv.compile(schema);
var context = { data: data };
var valid = validate.call(context, data);

I now understand what you meant by "treating schema as a meta-schema" (and the data as a schema). It won't work because Ajv doesn't attempt to generate a final schema (see #22) because references can be (mutually) recursive. If the referenced schema doesn't contain $ref the validating code is inlined (for performance reasons) and if it does - it is compiled as a separate function (you can control this behaviour with inlineRefs option). But the schema object itself remains unchanged.

@epoberezkin
Copy link
Member

in 4.2.0

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

No branches or pull requests

3 participants