From dc950ebd033cfc89d5a9f453d4d8f0a1358be4f9 Mon Sep 17 00:00:00 2001 From: Laila Winner Date: Fri, 30 Oct 2015 15:05:43 -0700 Subject: [PATCH] WIP: Use json_schema instead of json-schema * `json_matchers` cannot easily be used concurrently with Heroku's JSON API tools, i.e. `prmd` and `committee`, because `json_matchers` makes different assumptions about the structure of the user's schemata. An example of an incompatibility can be found in https://github.com/thoughtbot/json_matchers/issues/25: `json_matchers` breaks when the `id` property is present within a schema, but the Heroku tools require the presence of the `id` property ([reference](https://github.com/interagent/prmd/blob/master/docs/schemata.md#meta-data)). This is happening because the libraries used to dereference JSON pointers behave differently. `json-schema`, the library we're currently using, appears to conform less strictly to the JSON Schema specification than the library the Heroku tools use, `json_schema`. One solution to this problem is to update `json_matchers` to use the same approach to validating schemata as the Heroku tools. 1. Use `json_schema` instead of `json-schema` to validate schemata 2. Update documentation to instruct readers to follow Heroku's guidelines for structuring schemata: https://github.com/interagent/prmd/blob/master/docs/schemata.md --- json_matchers.gemspec | 2 +- lib/json_matchers/matcher.rb | 27 ++++---- .../match_response_schema_spec.rb | 63 +++++++++++++++---- 3 files changed, 68 insertions(+), 24 deletions(-) diff --git a/json_matchers.gemspec b/json_matchers.gemspec index 3181245..9c00d24 100644 --- a/json_matchers.gemspec +++ b/json_matchers.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency("json-schema", "~> 2.2.5") + spec.add_dependency("json_schema") spec.add_dependency("activesupport", '>= 3.0.0') spec.add_development_dependency "bundler", "~> 1.7" diff --git a/lib/json_matchers/matcher.rb b/lib/json_matchers/matcher.rb index 89b75b0..c7b829a 100644 --- a/lib/json_matchers/matcher.rb +++ b/lib/json_matchers/matcher.rb @@ -1,4 +1,5 @@ -require "json-schema" +require "pry" +require "json_schema" module JsonMatchers class Matcher @@ -10,16 +11,20 @@ def initialize(schema_path, **options) def matches?(response) @response = response - JSON::Validator.validate!( - schema_path.to_s, - response.body, - options, - ) - rescue JSON::Schema::ValidationError => ex - @validation_failure_message = ex.message - false - rescue JSON::ParserError - raise InvalidSchemaError + begin + schema_data = JSON.parse(File.read(@schema_path.to_s)) + response_body = JSON.parse(@response.body) + json_schema = JsonSchema.parse!(schema_data) + json_schema.expand_references + json_schema.validate!(response_body) + rescue RuntimeError => ex + @validation_failure_message = ex.message + return false + rescue JsonSchema::SchemaError, JSON::ParserError => ex + raise InvalidSchemaError + end + + true end def validation_failure_message diff --git a/spec/json_matchers/match_response_schema_spec.rb b/spec/json_matchers/match_response_schema_spec.rb index 2fdb5ea..dc395c3 100644 --- a/spec/json_matchers/match_response_schema_spec.rb +++ b/spec/json_matchers/match_response_schema_spec.rb @@ -95,22 +95,61 @@ end it "supports $ref" do - create_schema("single", { + create_schema("user", { + "id" => "user", "type" => "object", - "required" => ["foo"], - "properties" => { - "foo" => { "type" => "string" }, - } + "definitions" => { + "id" => { + "description" => "A unique identifier", + "example" => "1", + "type" => "integer" + } + }, + "required" => ["id"], + "properties" => { "id" => { "$ref" => "#/definitions/id" } } }) - create_schema("collection", { - "type" => "array", - "items" => { "$ref" => "single.json" }, + create_schema("users", { + "id" => "users", + "type" => "object", + "definitions" => { + "users" => { + "description" => "A collection of users", + "example" => [{ "id" => "1" }], + "type" => "array", + "items" => { "$ref" => "/schemas/user.json#" } + } + }, + "required" => ["users"], + "properties" => { "users" => { "$ref" => "#/definitions/users" } } }) - valid_response = response_for([{ "foo" => "is a string" }]) - invalid_response = response_for([{ "foo" => 0 }]) + valid_response = response_for([{ "id" => "1" }]) + invalid_response = response_for([{ "id" => "invalid" }]) - expect(valid_response).to match_response_schema("collection") - expect(invalid_response).not_to match_response_schema("collection") + expect(valid_response).to match_response_schema("users") + expect(invalid_response).not_to match_response_schema("users") + end + + it "supports the 'id' keyword" do + top_level_schema = { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "a": { "$ref": "#/nested.json" } + } + } + nested_schema = { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "required": ["b"], + "properties": { "b": { "type": "string" } }, + "id": "nested" + } + response_json = { a: { b: "foo" } } + create_schema("schema-with-id", top_level_schema) + create_schema("nested", nested_schema) + + expect(response_for(response_json)). + to match_response_schema("schema-with-id") end end