Skip to content

Commit e6eb1ef

Browse files
committed
URI Template resolution as pseudocode
This consolidates all of the URI Template resolution sections into one section. The new section is at the top level, as it applies to both LDO keywords ("href", "anchor", and all of the algorithm modifiers) and to a schema keyword ("base"). As much of the algorithm as possible is now given in pseudocode, although whether this is the most readable form is debatable. It is certainly more concise, and forced detailed consideration of some steps which had been under-specified, particularly with respect to "hrefSchema". The main changes are that setting any applicable subschema for a template variable to "false" in "hrefSchema" excludes that variable from accepting input, and that the input data for "hrefSchema" is prepopulated only with instance data that validates against all applicable subschemas within "hrefSchema". Expressing this properly requires the "applicability" concept from PR json-schema-org#424. Previously, the only mention of how to exclude a field from being used to pre-populate input was the advice to set the "properties" subschema to "false". However, this does not hold up once you start using more complex schemas.
1 parent 884b964 commit e6eb1ef

File tree

1 file changed

+176
-166
lines changed

1 file changed

+176
-166
lines changed

jsonschema-hyperschema.xml

Lines changed: 176 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -353,60 +353,6 @@
353353
at runtime, for instance due to application state that controls the operation's
354354
availability.
355355
</t>
356-
<section title="Resolving templated URIs">
357-
<t>
358-
URI Template variables in <xref target="href">"href"</xref> resolve from
359-
server-supplied instance data by default. This data is drawn from the
360-
sub-instance that validated against the schema containing the LDO.
361-
</t>
362-
<t>
363-
<xref target="templatePointers">"templatePointers"</xref> allows adjusting
364-
the location from which instance data is resolved on a per-variable
365-
basis.
366-
</t>
367-
<t>
368-
<xref target="hrefSchema">"hrefSchema"</xref> allows a link to specify
369-
a schema for resolving template variables from client-supplied data.
370-
Regular JSON Schema validation features can be used to require resolution
371-
from user agent data, forbid it, or allow user agent data while falling back
372-
to server-supplied instance data if no user agent data is provided.
373-
</t>
374-
<t>
375-
To implement the common pattern of resolving a templated path component
376-
with server-supplied instance data while accepting user agent data to build
377-
a query string:
378-
<list style="symbols">
379-
<t>
380-
set the "hrefSchema" subschemas for the path template variables
381-
to false, to disallow user agent input
382-
</t>
383-
<t>
384-
give the query string template variables names that do not appear
385-
in the instance, to prevent resolving them from the instance
386-
</t>
387-
</list>
388-
See the "hrefSchema" section for an example of this approach.
389-
</t>
390-
<t>
391-
To implement the equivalent of an input form pre-populated with
392-
pre-existing instance data:
393-
<list style="symbols">
394-
<t>
395-
ensure that each variable in the form resolves from the appropriate
396-
field in the instance, which provides the initial value that will
397-
continue to be used if the user agent takes no action to change it
398-
</t>
399-
<t> provide a validation schema for each of those fields in
400-
"hrefSchema", to describe what user agent input may be allowed
401-
to replace it
402-
</t>
403-
</list>
404-
This can be done with variables in any component of the URI template
405-
(path, query string, etc.) While the word "form" is used here,
406-
JSON Hyper-Schema does not constraint the nature of this interaction, which
407-
may or may not involve rendering an interactive form.
408-
</t>
409-
</section>
410356
<section title="Manipulating the target resource representation">
411357
<t>
412358
In JSON Hyper-Schema, <xref target="targetSchema">"targetSchema"</xref>
@@ -451,112 +397,6 @@
451397
This property is REQUIRED.
452398
</t>
453399

454-
<section title="URI Templating">
455-
<t>
456-
<cref>
457-
The pre-processing rules present in earlier drafts have been removed due
458-
to their complexity and inability to address all limitations with URI
459-
templating.
460-
This section is subject to significant change in upcoming drafts to
461-
replace the old pre-processing with a comprehensive solution.
462-
</cref>
463-
</t>
464-
<t>
465-
The value of "href" is to be used as a URI Template, as defined in
466-
<xref target="RFC6570">RFC 6570</xref>.
467-
However, some special considerations apply:
468-
</t>
469-
470-
<section title="Values for substitution">
471-
<t>
472-
The URI Template is filled out using data from some combination of an
473-
external source and the instance.
474-
Where either instance data or user agent data may be used, this section
475-
will refer simply to "data" or to a "value".
476-
When the source is important, it is specified explicitly.
477-
478-
To allow the use of any object property (including the empty string) or
479-
array index, the following rules are defined:
480-
</t>
481-
482-
<t>
483-
For a given variable name in the URI Template, the value to use is
484-
determined as follows:
485-
<list>
486-
<t>
487-
If the data is an array, and the variable name is a
488-
representation of a non-negative integer, then the value at the
489-
corresponding array index MUST be used (if it exists).
490-
</t>
491-
<t>
492-
Otherwise, the variable name should be percent-decoded, and the
493-
corresponding object property MUST be used (if it exists).
494-
</t>
495-
</list>
496-
</t>
497-
498-
<t>
499-
If <xref target="hrefSchema">"hrefSchema"</xref> is present and
500-
user agent data is provided, the data MUST be a valid instance according
501-
to the value of "hrefSchema".
502-
Template variables, after the process listed above, MUST first
503-
be resolved from the user agent data instance. Any variables left
504-
unresolved MUST be resolved from the resource instance data.
505-
</t>
506-
507-
<section title="Converting to strings">
508-
<t>
509-
When any value referenced by the URI template is null, a boolean or
510-
a number, then it should first be converted into a string as
511-
follows:
512-
<list>
513-
<t>
514-
null values SHOULD be replaced by the text "null"
515-
</t>
516-
<t>
517-
boolean values SHOULD be replaced by their lower-case
518-
equivalents: "true" or "false"
519-
</t>
520-
<t>
521-
numbers SHOULD be replaced with their original JSON
522-
representation.
523-
</t>
524-
</list>
525-
</t>
526-
<t>
527-
In some software environments the original JSON representation of a
528-
number will not be available (there is no way to tell the difference
529-
between 1.0 and 1), so any reasonable representation should be used.
530-
Schema and API authors should bear this in mind, and use other types
531-
(such as string or boolean) if the exact representation is
532-
important.
533-
</t>
534-
</section>
535-
</section>
536-
537-
<section title="Missing values" anchor="missingValues">
538-
<t>
539-
Sometimes, the appropriate values will not be available. In many
540-
cases, the URI Template behavior of simply removing variables that
541-
do not have a value will be appropriate. An example of this is
542-
optional query parameters, the presence or absence of which does
543-
not change the nature of the link relation type.
544-
</t>
545-
546-
<t>
547-
However, some variables, such as an identifier used in a path component,
548-
cannot meaningfully be omitted. The resulting URI would be meaningless,
549-
or would require a different link relation type. While "hrefSchema" can
550-
express a requirement for those variables that can be supplied via input,
551-
some variables must be resolved from instance data. When that instance
552-
data is not required by the context schema, the
553-
<xref target="templateRequired">"templateRequired</xref> keyword may
554-
be used to indicate that when the instance data is not available, the
555-
link does not apply.
556-
</t>
557-
</section>
558-
</section>
559-
560400
</section>
561401

562402
<section title="templatePointers" anchor="templatePointers">
@@ -676,11 +516,21 @@
676516
any user agent data from being accepted.
677517
</t>
678518
<t>
679-
Implementations MUST NOT attempt to validate values resolved from
680-
resource instance data with "hrefSchema". This allows for different
519+
Setting any subschema that applies to a particular variable to "false"
520+
prevents any user agent data from being accepted for that single variable.
521+
</t>
522+
<t>
523+
For template variables that can be resolved from the instance data,
524+
if the instance data is valid against all applicable subschemas
525+
in "hrefSchema", then it MUST be used to pre-populate the input
526+
data set for that variable.
527+
</t>
528+
<t>
529+
Note that even when data is pre-populated from the instance, the validation
530+
schema for that variable in "hrefSchema" need not be identical to the validation
531+
schema(s) that apply to the instance data's location. This allows for different
681532
validation rules for user agent data, such as supporting spelled-out
682-
months for date-time input but using the standard date-time
683-
format for storage.
533+
months for date-time input, but using the standard date-time format for storage.
684534
</t>
685535
<figure>
686536
<preamble>
@@ -711,7 +561,7 @@
711561
</figure>
712562
<figure>
713563
<preamble>
714-
In this example, the schema for "extra" is given as a reference
564+
In the following example, the schema for "extra" is given as a reference
715565
to keep the user agent data validation constraints identical to the
716566
instance validation constraints for the corresponding property,
717567
while "id" is given a false schema to prevent user agent data for
@@ -957,7 +807,7 @@ GET /foo/
957807
<t>
958808
This property changes the point within the instance that is considered
959809
to be the context resource of the link. The value of the property MUST be a
960-
valid <xref target="RFC6901">JSON Pointer</xref>, or a valid
810+
valid <xref target="RFC6901">JSON Pointer</xref>, or a valid
961811
<xref target="I-D.luff-relative-json-pointer">Relative JSON Pointer</xref>
962812
which is evaluated relative to the position in the instance from which
963813
<xref target="href">"href"</xref> template variable resolution would
@@ -1539,6 +1389,166 @@ GET /foo/
15391389
</t>
15401390
</section>
15411391
</section>
1392+
<section title="URI Templating">
1393+
<t>
1394+
Three hyper-schema keywords are <xref target="RFC6570">URI Templates</xref>:
1395+
"base", "anchor", and "href". Each are resolved separately to URI-references,
1396+
and then the anchor or href URI-reference is resolved against the base (which
1397+
is itself resolved against earlier bases as needed, each of which was first
1398+
resolved from a URI Template to a URI-reference).
1399+
</t>
1400+
<t>
1401+
All three keywords share the same algorithm for resolving variables from
1402+
instance data, which makes use of the "templatePointers" and "templateRequired"
1403+
keywords. When resolving "href", both it and any "base" templates
1404+
needed for resolution to an absolute URI, the algorithm is modfied to
1405+
optionally accept user input based on the "hrefSchema" keyword.
1406+
</t>
1407+
<t>
1408+
For each URI Template (T), the following pseudocode describes an algorithm for
1409+
resolving T into a URI-reference (R). For the purpose of this algorithm:
1410+
<list style="symbols">
1411+
<t>
1412+
"ldo.templatePointers" is an empty object if the keyword was not
1413+
present and "ldo.templateRequired" is likewise an empty array.
1414+
</t>
1415+
<t>
1416+
"attachmentPointer" is the absolute JSON Pointer for the attachment
1417+
location of the LDO.
1418+
</t>
1419+
<t>
1420+
"getApplicableSchemas()" returns an iterable set of all (sub)schemas
1421+
that apply to the attachment point in the instance.
1422+
</t>
1423+
</list>
1424+
</t>
1425+
<t>
1426+
This algorithm should be applied first to either "href" or "anchor",
1427+
and then as needed to each successive "base". The order is important,
1428+
as it is not always possible to tell whether a template will resolve
1429+
to a full URI or a URI-reference.
1430+
</t>
1431+
<figure>
1432+
<preamble>
1433+
This is the high-level algorithm. "T" comes from either "href"
1434+
or "anchor" within the LDO, or from "base" in a containing schema.
1435+
Pseudocode for each step follows.
1436+
</preamble>
1437+
<artwork>
1438+
<![CDATA[
1439+
templateData = populateDataFromInstance(T, ldo, instance)
1440+
1441+
if resolving "href" and ldo.hrefSchema exists:
1442+
inputData = acceptInput(ldo, instance, templateData)
1443+
for varname in inputData:
1444+
templateData[varname] = inputData[varname]
1445+
1446+
for varname in ldo.templateRequired:
1447+
if not exists templateData[varname]
1448+
fatal("Missing required variable(s)")
1449+
1450+
templateData = stringEncode(templateData)
1451+
R = rfc6570ResolutionAlgorithm(T, templateData)
1452+
]]>
1453+
</artwork>
1454+
</figure>
1455+
1456+
<section title="Populating template data from the instance">
1457+
<figure>
1458+
<artwork>
1459+
<![CDATA[
1460+
for varname in T:
1461+
varname = rfc3986PercentDecode(varname)
1462+
if varname in ldo.templatePointers:
1463+
valuePointer = templatePointers[varname]
1464+
if valuePointer is relative:
1465+
valuePointer = resolveRelative(attachmentPointer,
1466+
valuePointer)
1467+
else
1468+
valuePointer = attachmentPointer + "/" + varname
1469+
1470+
value = instance.valueAt(valuePointer)
1471+
if value is defined:
1472+
templateData[varname] = value
1473+
]]>
1474+
</artwork>
1475+
</figure>
1476+
</section>
1477+
1478+
<section title="Accepting input for template data">
1479+
<figure>
1480+
<preamble>
1481+
"InputForm" represents whatevers sort of input mechanism is appropriate.
1482+
This may be a literal web form, or may be a more programmatic construct
1483+
such as a callback funciton accepting specific fields and data types,
1484+
with the given initial values, if any.
1485+
</preamble>
1486+
<artwork>
1487+
<![CDATA[
1488+
form = new InputForm()
1489+
for varname in T:
1490+
useField = true
1491+
useInitialData = true
1492+
for schema in getApplicableSchemas(ldo.hrefSchema,
1493+
"/" + varname):
1494+
if schema is false:
1495+
useField = false
1496+
break
1497+
1498+
if varname in templateData and
1499+
not isValid(templateData[varname], schema)):
1500+
useInitialData = false
1501+
break
1502+
1503+
if useField:
1504+
if useInitialData:
1505+
form.addInputFieldFor(varname, ldo.hrefSchema,
1506+
templateData[varname])
1507+
else:
1508+
form.addInputFieldFor(varname, ldo.hrefSchema)
1509+
1510+
inputData = form.acceptInput()
1511+
1512+
if not isValid(inputData, hrefSchema):
1513+
fatal("Input invalid, link is not usable")
1514+
1515+
return inputData:
1516+
]]>
1517+
</artwork>
1518+
</figure>
1519+
</section>
1520+
1521+
<section title="Encoding data as strings">
1522+
<figure>
1523+
<artwork>
1524+
<![CDATA[
1525+
for varname in templateData:
1526+
value = templateData[varname]
1527+
if value is true:
1528+
templateData[varname] = "true"
1529+
else if value is false:
1530+
temlateData[varname] = "false"
1531+
else if value is null:
1532+
templateData[varname] = "null"
1533+
else if value is a number:
1534+
templateData[varname] =
1535+
bestEffortOriginalJsonString(value)
1536+
else:
1537+
templateData[varname] = rfc3986PercentEncode(value)
1538+
]]>
1539+
</artwork>
1540+
<postamble>
1541+
In some software environments the original JSON representation of a
1542+
number will not be available (there is no way to tell the difference
1543+
between 1.0 and 1), so any reasonable representation should be used.
1544+
Schema and API authors should bear this in mind, and use other types
1545+
(such as string or boolean) if the exact representation is
1546+
important. If the number was provide as input in the form of a
1547+
string, the string used as input SHOULD be used.
1548+
</postamble>
1549+
</figure>
1550+
</section>
1551+
</section>
15421552

15431553
<!--
15441554
<section title="IANA Considerations">

0 commit comments

Comments
 (0)