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

Support URI input with "hrefSchema" #228

Merged
merged 2 commits into from
Jan 23, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions hyper-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
"description": "a URI template, as defined by RFC 6570, with the addition of the $, ( and ) characters for pre-processing",
"type": "string"
},
"hrefSchema": {
"description": "a schema for validating user input to the URI template, where the input is in the form of a JSON object with property names matching variable names in \"href\"",
"allOf": [ {"$ref": "#"} ]
},
"rel": {
"description": "relation to the target resource of the link",
"type": "string"
Expand Down
106 changes: 102 additions & 4 deletions jsonschema-hyperschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,8 @@

<section title="Values for substitution">
<t>
After pre-processing, the URI Template is filled out using data from the instance.
After pre-processing, the URI Template is filled out using data from some combination of user input and the instance.

To allow the use of any object property (including the empty string), array index, or the instance value itself, the following rules are defined:
</t>

Expand All @@ -487,6 +488,14 @@
</list>
</t>

<t>
If <xref target="hrefSchema">"hrefSchema"</xref> is present and
user input is provided, the input MUST be valid according to the value of "hrefSchema".
Template variables, after the process listed above, MUST first
be resolved from the user input instance. Any variables left
unresolved MUST be resolved from the resource instance.
</t>

<section title="Converting to strings">
<t>
When any value referenced by the URI template is null, a boolean or a number, then it should first be converted into a string as follows:
Expand All @@ -506,18 +515,103 @@
<section title="Missing values">
<t>
Sometimes, the appropriate values will not be available.
For example, the template might specify the use of object properties, but the instance is an array or a string.
For example, the template might specify the use of object properties, but no such input was provide (or "hrefSchema" is not present), and the instance is an array or a string.
</t>

<t>
If any of the values required for the template are not present in the JSON instance, then substitute values MAY be provided from another source (such as default values).
If any of the values required for the template are present in neither the user input (if relevant) or the JSON instance, then substitute values MAY be provided from another source (such as default values).
Otherwise, the link definition SHOULD be considered not to apply to the instance.
</t>
</section>
</section>

</section>

<section title="hrefSchema" anchor="hrefSchema">
<t>
The value of the "hrefSchema" link description property MUST be
a valid JSON Schema. This schema is used to validate user input
for filling out the URI Template in
<xref target="href">"href"</xref>, as described in that section.
</t>
<t>
Omitting "hrefSchema" or setting the entire schema to "false" prevents
any user input from being accepted.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels different to how we normally do things. If I read that right, you're saying that omitting hrefSchema will always result in a validation fail if user input is provided. This now means that the URI template is useless without defining the validation with hrefSchema, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Relequestual no, it just means that you can't provide user input to resolve the template. In other words, if "hrefSchema" is absent, everything works exactly the same as it does now. That seemed preferable, although I take your point that we usually allow things unless forbidden.

I was also kind of going by how I think "schema" works. I think that if "schema" is not present, then there is no input allowed via that route. But going back and reading it, it's not really clear whether that is intended to be 'you can only provide input if "schema" is present' or 'if there is no schema, you can provide any input'.

Given how "schema" and "encType" are generally described, I think it/they need to be present for input to be allowed. If that is indeed the case, then "hrefSchema" should have the same behavior. If that is not the case, and a missing "schema" means any and all input is allowed, then yes, "hrefSchema" should be changed to behave that way as well.

Either way, we should also clarify the behavior when "schema" is absent.

Copy link
Contributor Author

@handrews handrews Jan 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Relequestual I figured out why it made sense to me to do it this way.

Validation is a constraint system. An empty schema validates everything, and every keyword you add restricts the set in some way.

Hyper-schema defines functionality. So in my head thats an additive capability approach. A blank hyper-schema indicates that there are no known hypermedia transitions, not that all possible transitions should work. So to me, it's philosophically consistent to look at a link and say that a minimal link defines minimal functionality. In order to know that it takes user input, it has to declare that it does.

I can see an argument in the other direction, though. I just wanted to explain why it felt consistent to me. I view it as a different conceptual model than validation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@handrews @Relequestual

Hyper-schema defines functionality. So in my head thats an additive capability approach. A blank hyper-schema indicates that there are no known hypermedia transitions, not that all possible transitions should work. So to me, it's philosophically consistent to look at a link and say that a minimal link defines minimal functionality.

One reason to take the additive approach that @handrews outlines is that it forces the user to define a schema whenever there are URI template variables to resolve, I think that is a nice feature because otherwise it suggests that you can provide any data whatsoever, where the object 's properties must coincidentally align with the URI template variables in run-time, sort of how it's set up currently. Perhaps the schema keyword has been previously used for this purpose in addition to validation, I can't really say. If this is true, I believe preventing schema from being used for multiple purposes is a good thing, that way one turning knob doesn't have two potential effects.

In order to know that it takes user input, it has to declare that it does.

I agree, the template alone isn't enough to say "I accept some data" because you're then left with the question of "well what kind of data do you accept?". In an ecosystem essentially based around defining comparable types, I'd argue it's pretty essential. Perhaps we could also say: "hrefSchema is only required and used for validation if there are URI template variables to resolve, otherwise it is / should be ignored"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the template alone isn't enough to say "I accept some data" because you're then left with the question of "well what kind of data do you accept?".

Yes, the naïve answer which I tried when implementing a previous project is to just look up the part of the instance schema that corresponds to the instance data that you would use to resolve that template variable. If you have a simple object for your instance, that's easy enough to do. But if you have some complex beast of "oneOf"s and "not" and "dependencies", etc., it's challenging. And not even possible unless you have an instance available.

It was very messy to implement, hence me deciding to require that input schemas be specified on their own rather than inferred. Explicit is better than implicit.

</t>
<t>
Implementations MUST NOT attempt to validate values resolved from
instance data with "hrefSchema". This allows for different
validation rules for user input, such as supporting spelled-out
months for date-time input but using the standard date-time
format for storage.
</t>
<figure>
<preamble>
For example, this defines a schema for each of the query string
parameters in the URI template:
</preamble>
<artwork>
<![CDATA[{
"href": "/foos{?condition,count,query}",
"hrefSchema": {
"properties": {
"condition": {
"type": "boolean",
"default": true
},
"count": {
"type": "integer",
"minimum": 0,
"default": 0
},
"query": {
"type": "string"
}
}
}
}]]>
</artwork>
</figure>
<figure>
<preamble>
In this example, the schema for "extra" is given as a reference
to keep the user input validation constraints identical to the
instance validation constraints for the corresponding property,
while "id" is given a false schema to prevent user input for
that variable.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the first part of this explanation, but not the second part.

..."id" is given a false schema to prevent user input for that variable

For easy reference...

...
"href": "/things/{id}{?extra}",
        "hrefSchema": {
            "properties": {
                "id": false,
                "extra": {"$ref": "#/definitions/extra"}
            }
        }
...

Maybe this is my lack of understand of HyperSchema, but how does having it in hrefSchema allow it as input? In terms of input in the URL? If so, is it implying that an invalid URI is things/123 while a valid URI is things/abc? This is really unclear and confusing to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, @Relequestual and I talked on IRC and identified some unclear wording that led to his confusion. I am going to rework that and then update this PR.

</preamble>
<artwork>
<![CDATA[{
"definitions": {
"extra": {
"type": "string",
"maxLength": 32
}
},
"type": "object",
"properties": {
"id": {
"type": "integer",
"minimum": 1,
"readOnly": true
},
"extra": {"$ref": "#/definitions/extra"}
},
"links": [{
"rel": "self",
"href": "/things/{id}{?extra}",
"hrefSchema": {
"properties": {
"id": false,
"extra": {"$ref": "#/definitions/extra"}
}
}
}]
}]]>
</artwork>
</figure>
</section>

<section title="rel">
<t>
The value of the "rel" property indicates the name of the relation to the target resource. The value MUST be a registered link relation from the <xref target="RFC5988">IANA Link Relation Type Registry established in RFC 5988</xref>, or a normalized URI following the <xref target="RFC3986">URI production of RFC 3986</xref>.
Expand Down Expand Up @@ -845,7 +939,10 @@ GET /foo/
</t>

<t>
Note that this does not provide data for any URI templates.
Note that this does not provide data for any URI templates. That is handed by <xref target="hrefSchema">"hrefSchema"</xref>. If the method is "get" and the resolved URI Template has a query string, the query string produced by input validated agaisnt "schema" replaces the existing query string.
</t>

<t>
This is a separate concept from the "targetSchema" property, which is describing the target information resource (including for replacing the contents of the resource in a PUT request), unlike "schema" which describes the user-submitted request data to be evaluated by the resource.
</t>
</section>
Expand Down Expand Up @@ -916,6 +1013,7 @@ GET /foo/
<t hangText="draft-wright-json-schema-hyperschema-01">
<list style="symbols">
<t>Fixed examples</t>
<t>Added "hrefSchema" for user input to "href" URI Templates</t>
</list>
</t>
<t hangText="draft-wright-json-schema-hyperschema-00">
Expand Down
4 changes: 4 additions & 0 deletions links.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
"description": "a URI template, as defined by RFC 6570, with the addition of the $, ( and ) characters for pre-processing",
"type": "string"
},
"hrefSchema": {
"description": "a schema for validating user input to the URI template, where the input is in the form of a JSON object with property names matching variable names in \"href\"",
"allOf": [ {"$ref": "#"} ]
},
"rel": {
"description": "relation to the target resource of the link",
"type": "string"
Expand Down