brandur / json_schema

A JSON Schema V4 and Hyperschema V4 parser and validator.
MIT License
230 stars 45 forks source link

v0.14.3 falis to extract links #82

Closed hokuma closed 7 years ago

hokuma commented 7 years ago

v0.14.3 fails to extract links.

require 'json_schema'
schema_data = JSON.parse(File.read('./sample_schema.json'))
schema = JsonSchema.parse!(schema_data)
schema.expand_references!
schema.definitions['app']
=> #<JsonSchema::Schema pointer=#/definitions/app>
irb(main):010:0> schema.definitions['app'].links
TypeError: no implicit conversion of nil into String
    from /Users/ohkuma/workspace/json_schema/lib/json_schema/schema.rb:249:in `+'
    from /Users/ohkuma/workspace/json_schema/lib/json_schema/schema.rb:249:in `pointer'
    from /Users/ohkuma/workspace/json_schema/lib/json_schema/schema.rb:203:in `inspect'
    from /Users/ohkuma/.rbenv/versions/2.2.4/bin/irb:11:in `<main>'

This is sample_schema.json(extracted from https://github.com/brandur/json_schema/blob/master/test/data_scaffold.rb#L8 )

{
    "$schema": "http://json-schema.org/draft-04/hyper-schema",
    "title": "Example API",
    "description": "An example API.",
    "type": [
        "object"
    ],
    "definitions": {
        "app": {
            "$schema": "http://json-schema.org/draft-04/hyper-schema",
            "title": "App",
            "description": "An app.",
            "id": "schemata/app",
            "type": [
                "object"
            ],
            "definitions": {
                "config_vars": {
                    "patternProperties": {
                        "^\\w+$": {
                            "type": ["null", "string"]
                        }
                    }
                },
                "contrived": {
                    "allOf": [{
                        "maxLength": 30
                    }, {
                        "minLength": 3
                    }],
                    "anyOf": [{
                        "minLength": 3
                    }, {
                        "minLength": 5
                    }],
                    "oneOf": [{
                        "pattern": "^(foo|aaa)$"
                    }, {
                        "pattern": "^(foo|zzz)$"
                    }],
                    "not": {
                        "pattern": "^$"
                    }
                },
                "contrived_plus": {
                    "allOf": [{
                        "$ref": "/schemata/app#/definitions/contrived/allOf/0"
                    }, {
                        "$ref": "/schemata/app#/definitions/contrived/allOf/1"
                    }],
                    "anyOf": [{
                        "$ref": "/schemata/app#/definitions/contrived/anyOf/0"
                    }, {
                        "$ref": "/schemata/app#/definitions/contrived/anyOf/1"
                    }],
                    "oneOf": [{
                        "$ref": "/schemata/app#/definitions/contrived/oneOf/0"
                    }, {
                        "$ref": "/schemata/app#/definitions/contrived/oneOf/1"
                    }],
                    "not": {
                        "$ref": "/schemata/app#/definitions/contrived/not"
                    }
                },
                "cost": {
                    "description": "running price of an app",
                    "example": 35.01,
                    "maximum": 1000.00,
                    "exclusiveMaximum": true,
                    "minimum": 0.0,
                    "exclusiveMinimum": false,
                    "multipleOf": 0.01,
                    "readOnly": false,
                    "type": ["number"]
                },
                "flags": {
                    "description": "flags for an app",
                    "example": ["websockets"],
                    "items": {
                        "pattern": "^[a-z][a-z\\-]*[a-z]$"
                    },
                    "maxItems": 10,
                    "minItems": 1,
                    "readOnly": false,
                    "type": ["array"],
                    "uniqueItems": true
                },
                "id": {
                    "description": "integer identifier of an app",
                    "example": 1,
                    "maximum": 10000,
                    "exclusiveMaximum": false,
                    "minimum": 0,
                    "exclusiveMinimum": true,
                    "multipleOf": 1,
                    "readOnly": true,
                    "type": ["integer"]
                },
                "identity": {
                    "anyOf": [{
                        "$ref": "/schemata/app#/definitions/id"
                    }, {
                        "$ref": "/schemata/app#/definitions/name"
                    }]
                },
                "name": {
                    "default": "hello-world",
                    "description": "unique name of app",
                    "example": "name",
                    "maxLength": 30,
                    "minLength": 3,
                    "pattern": "^[a-z][a-z0-9-]{3,30}$",
                    "readOnly": false,
                    "type": ["string"]
                },
                "owner": {
                    "description": "owner of the app",
                    "format": "email",
                    "example": "dwarf@example.com",
                    "readOnly": false,
                    "type": ["string"]
                },
                "production": {
                    "description": "whether this is a production app",
                    "example": false,
                    "readOnly": false,
                    "type": ["boolean"]
                },
                "role": {
                    "description": "name of a role on an app",
                    "example": "collaborator",
                    "readOnly": true,
                    "type": ["string"]
                },
                "roles": {
                    "additionalProperties": true,
                    "patternProperties": {
                        "^\\w+$": {
                            "$ref": "/schemata/app#/definitions/role"
                        }
                    }
                },
                "ssl": {
                    "description": "whether this app has SSL termination",
                    "example": false,
                    "readOnly": false,
                    "type": ["boolean"]
                },
                "visibility": {
                    "description": "the visibility of hte app",
                    "enum": ["private", "public"],
                    "example": false,
                    "readOnly": false,
                    "type": ["string"]
                }
            },
            "properties": {
                "config_vars": {
                    "$ref": "/schemata/app#/definitions/config_vars"
                },
                "contrived": {
                    "$ref": "/schemata/app#/definitions/contrived"
                },
                "cost": {
                    "$ref": "/schemata/app#/definitions/cost"
                },
                "flags": {
                    "$ref": "/schemata/app#/definitions/flags"
                },
                "id": {
                    "$ref": "/schemata/app#/definitions/id"
                },
                "name": {
                    "$ref": "/schemata/app#/definitions/name"
                },
                "owner": {
                    "$ref": "/schemata/app#/definitions/owner"
                },
                "production": {
                    "$ref": "/schemata/app#/definitions/production"
                },
                "ssl": {
                    "$ref": "/schemata/app#/definitions/ssl"
                },
                "visibility": {
                    "$ref": "/schemata/app#/definitions/visibility"
                }
            },
            "additionalProperties": false,
            "dependencies": {
                "production": "ssl",
                "ssl": {
                    "properties": {
                        "cost": {
                            "minimum": 20.0
                        },
                        "name": {
                            "$ref": "/schemata/app#/definitions/name"
                        }
                    }
                }
            },
            "maxProperties": 10,
            "minProperties": 1,
            "required": ["name"],
            "links": [{
                "description": "Create a new app.",
                "href": "/apps",
                "method": "POST",
                "rel": "create",
                "schema": {
                    "properties": {
                        "name": {
                            "$ref": "#/definitions/app/definitions/name"
                        }
                    }
                },
                "targetSchema": {
                    "$ref": "#/definitions/app"
                }
            }],
            "media": {
                "type": "application/json"
            },
            "pathStart": "/",
            "readOnly": false
        }
    },
    "properties": {
        "app": {
            "$ref": "#/definitions/app"
        }
    },
    "links": [{
        "href": "http://example.com",
        "rel": "self"
    }]
}
hokuma commented 7 years ago

Using with rails may cause this errors.

In non-rails project(Gemfile includes only json_schema), it works. In rails 4.1.15 and 5.0.0.1 with latest json_schema(0.14.3), fails to extract links. In rails 4.1.15 and 5.0.0.1 with downgraded json_schema(0.14.1), it works.

brandur commented 7 years ago

@hokuma Sorry to hear about the trouble!

Could you try cloning down this repository and seeing if you can reproduce the problem? https://github.com/brandur/json_schema-bug-reproduction-suite

I bundled Rails in and tried requiring some of its standard libraries, but haven't been able to reproduce yet.

hokuma commented 7 years ago

@brandur Thank you for your response.

I tried in https://github.com/brandur/json_schema-bug-reproduction-suite, and app.rb didn't cause any errors.

But calling inspect reproduced errors.

puts schema.definitions['app'].links.inspect

Originally, I saw this errors in rails console(same as irb) which use inspect for output. json_schema has the same method which is defined in here. It seems that this conflict cause the errors.

Altogether, there is no problem to extract links except irb enviroment.

brandur commented 7 years ago

@hokuma Aha! Thank you.

That actually makes sense. The test coverage on #inspect is atrocious right now and that whole method needs to be reimplemented. I'll take a look at a fix.

brandur commented 7 years ago

Fix released as 0.14.4. Sorry about the trouble here :/