ajv-validator / ajv

The fastest JSON schema Validator. Supports JSON Schema draft-04/06/07/2019-09/2020-12 and JSON Type Definition (RFC8927)
https://ajv.js.org
MIT License
13.76k stars 872 forks source link

Can't resolve reference inside a schema to an external .json file on a server #534

Closed flodan closed 7 years ago

flodan commented 7 years ago

I have the following problem: I want to build an Angular 2 service that loads json schemes from a server by simple http-get calls and stores them in json objects. These objects should then be added to the ajv instance to being able to perform a validation. My json schema api on the server side is structured in a folder with all top level json schemes and a folder next to these top level schemes, with all the referenced definitions:

What version of Ajv are you using? Does the issue happen if you use the latest version?

I use json-schema-draft-04

Ajv options object

this.ajvInstance = new Ajv( {
      meta: false,
      extendRefs: true,
      unknownFormats: 'ignore',
      allErrors: true
    });

JSON Schema

{
  "$id":          "http://url/to/folder/getPlan_V0.json",
  "title":        "getPlan_V0.0.6",
  "$schema":      "http://json-schema.org/draft-04/schema#",
  "type":         "object",
  "properties": {
    "session_id": {
      "$ref": "definitions/session_id_V0.json"
    },
    "barcode": {
      "$ref": "definitions/barcode_V0.json"
    }
  },
  "additionalProperties": false,
  "required": [ "session_id", "barcode" ]
}

The file session_id_V0.json is in a separate folder next to getPlan_V0.json:

{
  "title":        "session_id_V0.0.2",
  "$schema":      "http://json-schema.org/draft-04/schema#",
  "type":         "string",
  "minLength":    32,
  "maxLength":    32
}

Sample data

Your code The 2 schemes are already stored as json objects in the two variables given to .addSchema

let metaSchema = require( 'ajv/lib/refs/json-schema-draft-04.json' );
    this.ajvInstance.addMetaSchema( metaSchema );
    this.ajvInstance._opts.defaultMeta = metaSchema.id;
    this.ajvInstance.addSchema( this.json_schemes.getPlan_V0, 'getPlan_V0' );
    this.ajvInstance.addSchema( this.json_schemes_definitions.session_id_V0, 'session_id_V0.json' );

let valid = this.ajvInstance.validate( schema, object_to_validate );

    if( valid ) {
      returnValue = true;
    } else {
      returnValue = false;
    }

Validation result, data AFTER validation, error messages

Error: can't resolve reference definitions/session_id_V0.json from id http://url/to/folder/getPlan_V0.json#

What results did you expect? I expected valid to not being true, as the object_to_validate is not of the tested type.

Are you going to resolve the issue?

flodan commented 7 years ago

Additionally, is there a way of directly importing json schemes from a server to the ajv instance?

epoberezkin commented 7 years ago
  1. You are not using references correctly.

There are no definitions in your schema, so $ref cannot be resolved. If they are in a different file, full URI should be used with hash fragment.

  1. You can use compileAsync method - see the docs.
epoberezkin commented 7 years ago

If you want to reference the whole file, just use its name, why do you need "definitions/"? Also the second file is not added to Ajv instance.

flodan commented 7 years ago

The second file, which I want to reference to, is in the folder definitions. That is why I have the definitions in the $ref argument. Do I have to put the whole URL to the file in the$ref argument?

Sent from my HUAWEI MHA-L29 using FastHub

epoberezkin commented 7 years ago

I can't say without looking at files. You have to explicitly add all the referenced files using addSchema method and the resolved URI (using schema id as base URI - see RFC3986) should be the same as id in the added file. This is all according to JSON Schema spec. If you use the method compileAsync the schemas will be downloaded from full resolved URIs using the hook you passed via options.

epoberezkin commented 7 years ago

Also your schema is draft-06 (you use $id) and you use $schema for draft-04, although that is not what is not working here - you simply use incorrect references (most likely :).

flodan commented 7 years ago

Thank you so far for your support!! I now changed my code to use compileAsync with the exact same function loadSchema as provided by your example, but I get the following error

Unhandled Promise rejection: request.json is not a function ; Zone: ProxyZone ; Task: Promise.then ; Value: TypeError: request.json is not a function
    at Object.loadSchema (VM124 main.bundle.js:2741)
    at loadMissingSchema (VM123 vendor.bundle.js:688)
    at _compileAsync (VM123 vendor.bundle.js:677)
    at VM123 vendor.bundle.js:653
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (VM115 polyfills.bundle.js:2794)
    at ProxyZoneSpec.webpackJsonp.../../../../zone.js/dist/proxy.js.ProxyZoneSpec.onInvoke (VM123 vendor.bundle.js:69461)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (VM115 polyfills.bundle.js:2793)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run (VM115 polyfills.bundle.js:2544)
    at VM115 polyfills.bundle.js:3221
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (VM115 polyfills.bundle.js:2827) TypeError: request.json is not a function
    at Object.loadSchema (http://localhost:9876/_karma_webpack_/main.bundle.js:2741:24)
    at loadMissingSchema (http://localhost:9876/_karma_webpack_/vendor.bundle.js:688:64)
    at _compileAsync (http://localhost:9876/_karma_webpack_/vendor.bundle.js:677:48)
    at http://localhost:9876/_karma_webpack_/vendor.bundle.js:653:34
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/polyfills.bundle.js:2794:26)
    at ProxyZoneSpec.webpackJsonp.../../../../zone.js/dist/proxy.js.ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/vendor.bundle.js:69461:39)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/polyfills.bundle.js:2793:32)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run (http://localhost:9876/_karma_webpack_/polyfills.bundle.js:2544:43)
    at http://localhost:9876/_karma_webpack_/polyfills.bundle.js:3221:57
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (http://localhost:9876/_karma_webpack_/polyfills.bundle.js:2827:31)

Do you have any idea what that could be?