Skip to content

Commit 7546fb0

Browse files
authored
Merge pull request #448 from handrews/collection
Add most of a collection example.
2 parents 673fb51 + 9b85df8 commit 7546fb0

File tree

1 file changed

+298
-2
lines changed

1 file changed

+298
-2
lines changed

jsonschema-hyperschema.xml

Lines changed: 298 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@
440440
<section title='"self" links' anchor="self">
441441
<t>
442442
A hyper-schema implementation MUST recognize that a link with relation
443-
type "self" that is applicable to the entire instance document describes
443+
type "self" that is applicable to the entire instance document describes
444444
how a user agent can interact with the resource represented by that
445445
instance document. A "self" link MUST be resolvable from the instance,
446446
and therefore "hrefSchema" MUST NOT be present.
@@ -1467,7 +1467,7 @@ Link: <https://schema.example.com/entry> rel=describedBy
14671467
<![CDATA[{
14681468
"$id": "https://schema.example.com/thing",
14691469
"$schema": "http://json-schema.org/draft-07-wip/hyper-schema#",
1470-
"base": "http://api.example.com",
1470+
"base": "https://api.example.com",
14711471
"type": "object",
14721472
"required": ["data"],
14731473
"properties": {
@@ -1724,6 +1724,302 @@ Link: <https://schema.example.com/entry> rel=describedBy
17241724
Discovering ordered links
17251725
Multiple self links (for the collection and each item)
17261726
</cref></t>
1727+
<t>
1728+
In many systems, individual resources are grouped into collections. Those
1729+
collections also often provide a way to create individual item resources with
1730+
server-assigned identifiers.
1731+
</t>
1732+
<t>
1733+
This schema describes a collection where each item representation is
1734+
identical to the individual resource item representation, and there
1735+
is enough metadata included in the collection representation to
1736+
produce pagination links. The "first" and "last" pagination links
1737+
were omitted as this is already a long example.
1738+
</t>
1739+
<t>
1740+
Note that there is an object member called "items", which is an array
1741+
and thefore uses the validation keyword "items" in its own schema. The outer
1742+
"items" is a property name, the inner one is a schema keyword.
1743+
</t>
1744+
<figure>
1745+
<artwork>
1746+
<![CDATA[{
1747+
"$id": "https://schema.example.com/thing-collection",
1748+
"$schema": "http://json-schema.org/draft-07-wip/hyper-schema#",
1749+
"base": "https://api.example.com",
1750+
"type": "object",
1751+
"required": ["items"],
1752+
"properties": {
1753+
"items": {
1754+
"type": "array",
1755+
"items": {
1756+
"allOf": [{"$ref": "thing#"}],
1757+
"links": [
1758+
{
1759+
"anchorPointer": "",
1760+
"rel": "item",
1761+
"href": "things/{id}",
1762+
"hrefRequired": ["id"],
1763+
"targetSchema": {"$ref": "thing#"}
1764+
}
1765+
]
1766+
}
1767+
},
1768+
"meta": {
1769+
"type": "object",
1770+
"properties": {
1771+
"prev": {"$ref": "#/definitions/scrolling"},
1772+
"current": {"$ref": "#/definitions/scrolling"},
1773+
"next": {"$ref": "#/definitions/scrolling"}
1774+
}
1775+
}
1776+
},
1777+
"links": [
1778+
{
1779+
"rel": "self",
1780+
"href": "things{?offset,limit}",
1781+
"hrefRequired": ["offset", "limit"],
1782+
"hrefPointers": {
1783+
"offset": "/meta/current/offset",
1784+
"limit": "/meta/current/limit"
1785+
},
1786+
"targetSchema": {"$ref": "#"}
1787+
},
1788+
{
1789+
"rel": "prev",
1790+
"href": "things{?offset,limit}",
1791+
"hrefRequired": ["offset", "limit"],
1792+
"hrefPointers": {
1793+
"offset": "/meta/prev/offset",
1794+
"limit": "/meta/prev/limit"
1795+
},
1796+
"targetSchema": {"$ref": "#"}
1797+
},
1798+
{
1799+
"rel": "next",
1800+
"href": "things{?offset,limit}",
1801+
"hrefRequired": ["offset", "limit"],
1802+
"hrefPointers": {
1803+
"offset": "/meta/next/offset",
1804+
"limit": "/meta/next/limit"
1805+
},
1806+
"targetSchema": {"$ref": "#"}
1807+
}
1808+
],
1809+
"definitions": {
1810+
"scrolling": {
1811+
"type": "object",
1812+
"properties": {
1813+
"offset": {
1814+
"type": "integer",
1815+
"minimum": 0,
1816+
"default": 0
1817+
},
1818+
"limit": {
1819+
"type": "integer",
1820+
"minimum": 1,
1821+
"maximum": 100,
1822+
"default": 10
1823+
}
1824+
}
1825+
}
1826+
}
1827+
}]]>
1828+
</artwork>
1829+
<postamble>
1830+
Notice that the "self" link includes the pagination query
1831+
that produced the exact representation, rather than being
1832+
a generic link to the collection allowing selecting the
1833+
page via input. There is no link for manual page selection
1834+
in the example as shown here, nor is there a "submissionSchema"
1835+
for item creation, but we will consider those additions further down.
1836+
</postamble>
1837+
</figure>
1838+
<figure>
1839+
<artwork>
1840+
<![CDATA[{
1841+
"items": [
1842+
{"id": 12345, "data": {}},
1843+
{"id": 67890, "data": {}}
1844+
],
1845+
"meta": {
1846+
"current": {
1847+
"offset": 0,
1848+
"limit": 2
1849+
},
1850+
"next": {
1851+
"offset": 3,
1852+
"limit": 2
1853+
}
1854+
}
1855+
}]]>
1856+
</artwork>
1857+
</figure>
1858+
<figure>
1859+
<preamble>
1860+
Here are all of the links that apply to this instance,
1861+
including those that are referenced by using the "thing"
1862+
schema for the individual items. The "self" links for
1863+
the overal resource and each individual item are
1864+
distinguished by different context pointers. Note also
1865+
that the "item" and "self" links for a given thing have
1866+
identical target URIs but different context pointers.
1867+
</preamble>
1868+
<artwork>
1869+
<![CDATA[[
1870+
{
1871+
"contextUri": "https://api.example.com/things",
1872+
"contextPointer": "",
1873+
"rel": "self",
1874+
"targetUri": "https://api.example.com/things?offset=20,limit=2",
1875+
"attachmentPointer": ""
1876+
},
1877+
{
1878+
"contextUri": "https://api.example.com/things",
1879+
"contextPointer": "",
1880+
"rel": "next",
1881+
"targetUri": "https://api.example.com/things?offset=22,limit=2",
1882+
"attachmentPointer": ""
1883+
},
1884+
{
1885+
"contextUri": "https://api.example.com/things",
1886+
"contextPointer": "",
1887+
"rel": "item",
1888+
"targetUri": "https://api.example.com/things/1234",
1889+
"attachmentPointer": "/items/0"
1890+
},
1891+
{
1892+
"contextUri": "https://api.example.com/things",
1893+
"contextPointer": "",
1894+
"rel": "item",
1895+
"targetUri": "https://api.example.com/things/67890",
1896+
"attachmentPointer": "/items/1"
1897+
},
1898+
{
1899+
"contextUri": "https://api.example.com/things",
1900+
"contextPointer": "/items/0",
1901+
"rel": "self",
1902+
"targetUri": "https://api.example.com/things/1234",
1903+
"attachmentPointer": "/items/0"
1904+
},
1905+
{
1906+
"contextUri": "https://api.example.com/things",
1907+
"contextPointer": "/items/1",
1908+
"rel": "self",
1909+
"targetUri": "https://api.example.com/things/67890",
1910+
"attachmentPointer": "/items/1"
1911+
}
1912+
]]]>
1913+
1914+
</artwork>
1915+
<postamble>
1916+
Note that there is no "prev" link in the output, as we are looking
1917+
at the first page. The lack of a "prev" field under "meta",
1918+
together with the "prev" link's "hrefRequired" values, means
1919+
that the link is not usable with this particular instance.
1920+
</postamble>
1921+
</figure>
1922+
<t>
1923+
To fully specify our collection, we also need to add the following
1924+
link to our "thing" schema. Note that this would cause it to also
1925+
appear as a link in each item in the collection representation, which
1926+
is a good example of why it is best to only construct links upon request.
1927+
There is no need for having as many functionally identical "collection"
1928+
links as there are items in a collection page on every collection
1929+
representation.
1930+
</t>
1931+
<figure>
1932+
<preamble>
1933+
This link would be added to the top-level "links" array in the
1934+
"https://schemasexample.com/thing" schema.
1935+
</preamble>
1936+
<artwork>
1937+
<![CDATA[{
1938+
"rel": "collection",
1939+
"href": "/things",
1940+
"targetSchema": {"$ref": "thing-collection#"},
1941+
"submissionSchema": {"$ref": "#"}
1942+
}]]>
1943+
</artwork>
1944+
<postamble>
1945+
Here we see the "submissionSchema" indicating that we can create
1946+
a single "thing" by submitting a representation (minus server-created
1947+
fields such as "id") to the collection that is this link's target
1948+
schema. While we cannot, in general, make assumptions about the
1949+
semantics of making a data submission request (in HTTP terms, a POST),
1950+
<xref target="collectionAndItem" /> tells us that we MAY make such
1951+
an assumption when the link relation is "collection", and that
1952+
hyper-schema authors MUST NOT use a "collection" link if the data
1953+
submission operation has semantics other than item creation.
1954+
</postamble>
1955+
</figure>
1956+
<t>
1957+
<cref>
1958+
I left off the scrolling parameters for this link. Technically,
1959+
the link should go to the page containing the specific "thing"
1960+
that is the context of the link. If the collection is using
1961+
semantic sorting, then this is do-able (ideally setting
1962+
the page boundary such that this is the first item, and allowing
1963+
input on the page count / "limit" parameter). But getting into
1964+
semantic scrolling/pagination seems way too involved for this
1965+
example.
1966+
</cref>
1967+
</t>
1968+
<t>
1969+
<cref>
1970+
Note also that POST-ing to a collection page that will not contain
1971+
the created item also seems weird. While retrieving the collection
1972+
from a query parameter-less URI will still retrieve a page, that's
1973+
a limit imposed by the server. POST-ing to such a URI and disregarding
1974+
the "default" values for the parameters seems correct. This is another
1975+
reason to *not* automatically write default values into the query.
1976+
</cref>
1977+
</t>
1978+
<t>
1979+
But what if we do not have any "thing"s yet? We cannot get to the
1980+
individual "thing" schema as there is no individual "thing" to fetch.
1981+
And the "tag:rel.example.com,2017:thing" link in the entry point
1982+
resource does not indicate how to create a "thing". The "self" link
1983+
requires the "id" to already exist, but it is assigned by the server.
1984+
So we need to add another link to our entry point schema:
1985+
</t>
1986+
<figure>
1987+
<preamble>
1988+
This LDO would be added to the top-level "links" array in the entry
1989+
point resource's hyper-schema.
1990+
</preamble>
1991+
<artwork>
1992+
<![CDATA[{
1993+
"rel": "tag:rel.example.com,2017:thing-collection",
1994+
"href": "/things{?offset,limit}",
1995+
"hrefSchema": {
1996+
"$ref": "thing-collection#/definitions/scrolling"
1997+
},
1998+
"submissionSchema": {
1999+
"$ref": "thing#"
2000+
},
2001+
"targetSchema": {
2002+
"$ref": "thing-collection#"
2003+
}
2004+
}]]>
2005+
</artwork>
2006+
<postamble>
2007+
Now we see the scrolling parameters being accepted as input, so
2008+
we can jump to any scroll window within the collection.
2009+
</postamble>
2010+
</figure>
2011+
<t>
2012+
<cref>
2013+
Here we also see the "submissionSchema" to use to create a "thing",
2014+
but how do we recognize it? We can't use a "collection" link relation
2015+
here, because there is no identifiable "thing" to serve as the context
2016+
resource. We could look at the submission schema, notice that it
2017+
has a "collection" link and is therefore an item, and notice that
2018+
its "collection" link produces the same(-ish?) URI, but that seems
2019+
overly complicated and also gets into trouble with query parameters
2020+
again.
2021+
</cref>
2022+
</t>
17272023
</section>
17282024
</section>
17292025

0 commit comments

Comments
 (0)