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

Changing URI Template resolution base #382

Closed
handrews opened this issue Aug 30, 2017 · 1 comment
Closed

Changing URI Template resolution base #382

handrews opened this issue Aug 30, 2017 · 1 comment

Comments

@handrews
Copy link
Contributor

This proposal recycles bits and pieces of several ideas, hopefully into a simpler, more focused form.

The topic has come up in #140 and #381 (about two different mechanisms for changing the link context, often to a different point within the same instance document). There are several examples involving collections and how they motivate this in #381 which I won't repeat here. An even more pathological case is a tree structure.

Here is one, with a link "href" designed to illustrate the problem rather than to be the best possible tree design. Please focus on the problem being illustrated instead of redesigning the tree by, say, putting parent and root ids in each node which would completely defeat the purpose.

The idea is to think about situations where adding more data all over a data structure is impractical, perhaps for reasons of constrained storage. Adding two more ids to every node would be significant bloat, and one of JSON Hyper-Schema's strengths is reducing payload size by keeping a lot of information out of the instance.

So, here is the minimal n-ary tree schema:

{
  "type": "object",
  "required": ["id"],
  "properties": {
    "id": {"type": "integer", "minimum": 0},
    "children": {"type": "array", "items": {"$ref": "#"}}
  },
  "links": [
    {
      "rel": "self",
      "href": "/trees{/root,parent,id}"
    }
  ]
}

The template variable starts with a "/", making it a path expansion variable. So each of the three variables (root, parent, and id) would become a path component, if a value is defined. If no value is defined, that path component is omitted (yes, having the middle component be optional is weird, just roll with it).

What this URI Template is trying to express is that every node's self URI includes the id of the root node, the id of the immediate parent, and the id of the actual node, in that order. Sometimes the parent node will be the same as the root. For the root node, there is no parent node so it should have only the two path components.

Of course, there is nothing called "root" or "parent" in the schema. So how would that work?

Here is an instance:

{
  "id": 0,
  "children": [ {
    "id": 1,
    "children": [ {
      "id": 2,
      "children": [ {
        "id": 3
} ] } ] } ] } 

The resolved self links that we would want are:

  • /trees/0/0
  • /trees/0/0/1
  • /trees/0/1/2
  • /trees/0/2/3

The proposal here is to add a field, perhaps called "hrefPointers", which adjusts the starting point for variable resolution.

  • For id, we do not need to adjust it; the default behavior of looking for a property with that name in the (sub)instance to which the link is attached always produces the correct value
  • For root, we need to adjust it to a known absolute location (the id field of the root of the instance)
  • For parent, we need to adjust it to a relative location, which may or may not exist

The value of this keyword would be an object mapping variable name components to Relative JSON Pointers (which we need not revive as its own spec- we can put the syntax in an appendix and if we get feedback that people would like the I-D revived, we can worry about it then). These relative pointers are applied to the instance, starting from the point at which the link is defined.

Also note that regular JSON Pointers are also valid Relative JSON Pointers. Since relative JSON pointers are meaningless as URI fragments (RFC 3986 has no provision for relative fragment references), these are plain JSON string pointers, not pointers encoded in URI fragments. Even when they are absolute pointers.

Here's what it would look like for this example. Note that

    {
      "rel": "self",
      "href": "/trees{/root,parent,id}",
      "hrefPointers": {
        "root": "/id",
        "parent": "2/id"
      }
    }
  • "id" does not appear in "hrefPointers", because it needs no adjusting
  • "root" is mapped to an absolute JSON Pointer to the id at the top level of the instance
  • "parent" is mapped to the id two levels above the point at which the link is attached
    • "0" is the point in the instance at which the link is attached (the actual node object)
    • "1" is one level above that (the children array of the parent node)
    • "2" is two levels above (the actual parent node object, containing the id and the children array)

As with all URI Template variable resolution, if the target of the variable does not exist, that variable is simply dropped. So for the root's self link, the parent is an invalid pointer and is just ignored, which is what we want.


In an earlier version of "hrefSchema", I had proposed using pointers like this with "$data", "const", and "default" to accomplish this. However @awwright pushed back on that (very wisely, in retrospect, even if I was frustrated at the time) and we struck an excellent compromise to put "hrefSchema" in purely for client input validation, and deferred adjusting instance resolution to a later draft.

I believe this proposal is far superior for several reasons:

  • It avoids "$data" completely. I always felt the Hyper-Schema use case in URI Template resolution was the most compelling application, but had trouble getting buy-in for the full $data proposal. I think "hrefPointers" meets @awwright's "principle of least power" concern.
  • It avoids relying on specific "default" behavior. This is always a controversial point, and not one I really wanted to have to "solve" for such a specific thing.
  • It keeps client input rules and instance resolution rules separate, as they are today. I have come to appreciate that separation more than I once did, for instance for things like allowing a variety of input formats for users to submit in query parameters, which turn into a standardized format in the instance.
  • It avoids needing to either determine the matching schema based on an instance position, or the matching instance position based on a schema, both of which are between challenging and impossible in the general case.

I am particularly excited that this would let us bump consideration of "$data" to the next draft. My focus for this draft is making hyper-schema fully usable, with a secondary focus around addlProps/re-use, and I don't see us having the time to consider "$data" now. This addresses a key hyper-schema requirement for me without it.

Also paging @dlax @philsturgeon

@handrews
Copy link
Contributor Author

Merged #386.

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

No branches or pull requests

1 participant