This document describes the xRegistry model definition language that is used to define custom Groups and Resources for use within an xRegistry.
Readers are strongly encouraged to read the core xRegistry specification to learn about xRegistry itself prior to reading this specification.
This specification defines the format and features of the xRegistry model language. The xRegistry model is used to define the custom Groups, Resources and attributes of the entities managed within an xRegistry service instance. It will also define the semantics, and constraints, of modifying an existing model.
This specification adheres to the Notational Conventions defined in the core xRegistry specification.
This specification uses the following terms as defined in the core xRegistry specification:
The Registry model defines the Groups, Resources, attributes and changes to specification-defined attributes that define what a Registry instance supports. This information is intended to be used by tooling that does not have knowledge of the structure of the Registry in advance and therefore will need to dynamically discover it.
The following sections will go into the details of how to create, retrieve and edit the model of a Registry, while the xRegistry protocol binding specifications will define how the operations defined in this specification will be mapped to those protocols.
The overall format of a model definition is as follows:
{
"description": "<STRING>", ?
"documentation": "<URL>", ?
"labels": { "<STRING>": "<STRING>" * }, ?
"attributes": { # Registry-level extensions
"<STRING>": { # Attribute name
"name": "<STRING>", # Same as attribute's key
"type": "<TYPE>", # boolean, string, array, object, ...
"target": "<XIDTYPE>", ? # If "type" is "xid" or "url"
"namecharset": "<STRING>", ? # If "type" is "object"
"description": "<STRING>", ?
"enum": [ <VALUE> * ], ? # Array of scalars of type "<TYPE>"
"strict": <BOOLEAN>, ? # Just "enum" values or not. Default=true
"matchcase": <BOOLEAN>, ? # Strings case-sensitive? Default=false
"readonly": <BOOLEAN>, ? # From client's POV. Default=false
"immutable": <BOOLEAN>, ? # Once set, can't change. Default=false
"required": <BOOLEAN>, ? # Default=false
"default": <VALUE>, ? # Scalar attribute's default value
"attributes": { ... }, ? # If "type" above is object
"item": { # If "type" above is map,array
"type": "<TYPE>", ? # Map value type, or array type
"target": "<XIDTYPE>", ? # If this item "type" is xid/url
"namecharset": "<STRING>", ? # If this item "type" is object
"attributes": { ... }, ? # If this item "type" is object
"item": { ... } ? # If this item "type" is map,array
}, ?
"ifvalues": { # If "type" is scalar
"<STRING>": {
"siblingattributes": { ... } # Siblings to this "attribute"
} *
} ?
} *
},
"groups": {
"<STRING>": { # Key=plural name, e.g. "endpoints"
"plural": "<STRING>", # E.g. "endpoints"
"singular": "<STRING>", # E.g. "endpoint"
"description": "<STRING>", ?
"documentation": "<URL>", ?
"icon": "<URL>", ?
"labels": { "<STRING>": "<STRING>" * }, ?
"modelversion": "<STRING>", ? # Version of the group model
"modelcompatiblewith": "<URI>", ? # Statement of compatibility
"attributes": { ... }, ? # See "attributes" above
"ximportresources": [ "<XIDTYPE>", * ], ? # Include these Resources
"resources": {
"<STRING>": { # Key=plural name, e.g. "messages"
"plural": "<STRING>", # E.g. "messages"
"singular": "<STRING>", # E.g. "message"
"description": "<STRING>", ?
"documentation": "<URL>", ?
"icon": "<URL>", ?
"labels": { "<STRING>": "<STRING>" * }, ?
"modelversion": "<STRING>", ? # Version of the resource model
"modelcompatiblewith": "<URI>", ? # Statement of compatibility
"maxversions": <UINTEGER>, ? # Num Vers(>=0). Default=0, 0=unlimited
"setversionid": <BOOLEAN>, ? # vid settable? Default=true
"setdefaultversionsticky": <BOOLEAN>, ? # Sticky settable? Default=true
"hasdocument": <BOOLEAN>, ? # Has separate document. Default=true
"versionmode": "<STRING>", ? # 'ancestor' processing algorithm
"singleversionroot": <BOOLEAN>, ? # Enforce single root. Default=false
"validateformat": <BOOLEAN>, ? # Do Version format checks. Default=false
"validatecompatibility": <BOOLEAN>, ? # Do Version compat checks. Default=false
"strictvalidation": <BOOLEAN>, ? # Block unknown format/compat. Default=false
"consistentformat": <BOOLEAN>, ? # Same format for all Vers. Default=false
"typemap": <MAP>, ? # ContentType mappings
"attributes": { ... }, ? # Version attributes/extensions
"resourceattributes": { ... }, ? # Resource attributes/extensions
"metaattributes": { ... } ? # Meta attributes/extensions
} *
} ?
} *
} ?
}
The following describes the attributes of the Registry model:
description 🔗labels 🔗attributes 🔗attributes.<STRING> 🔗attributes.<STRING>.name
for more information.attributes.<STRING>.name 🔗Type: String.
REQUIRED.
The name of the attribute. MUST be the same as the key used in the owning
attributes map. A value of * indicates support for undefined
extension names. Absence of a * attribute indicates lack of support for
undefined extensions and an error
(unknown_attribute) MUST be generated if
one is present in a request to update the Registry attributes.
Often * is used with a type of any to allow for any undefined
extension name of any supported data type. By default, the model
does not support undefined extensions. Note that undefined extensions, if
supported, MUST adhere to the same rules as
defined extensions.
An attribute of * MUST NOT use the ifvalues feature, but a non-*
attribute MAY define an ifvalues attribute named * as long as there
isn't already one defined for this level in the entity.
An extension attribute MUST NOT use a name that conflicts with any specification-defined attribute, sub-object attribute or collection-related attribute names defined at the same level in the hierarchy. For Resource/Version attributes, this applies for both levels - e.g. a Version-level extension MUST NOT use a name that conflicts with its Resource-level attribute names.
attributes.<STRING>.type 🔗attributes.<STRING>.target 🔗type is set to
url-reference, uri-reference or xid. target MUST NOT be used
for any other type of attribute./<GROUPS> - a plural Group type name. An entity attribute of this
type/target MUST reference an instance of this Group type./<GROUPS>/<RESOURCES> - a plural Resource type name. An entity
attribute of this type/target MUST reference an instance of this
Resource type, not a specific Version of the Resource./<GROUPS>/<RESOURCES>[/versions]. An entity attribute of this
type/target MUST reference either an instance of this Resource type or
an instance of a Version of this Resource type. Note the [/versions]
portion of the target value is that exact string, including the
square brackets./GROUPS/<RESOURCES>/versions - a Version of a Resource type. An entity
attribute of this type/target MUST reference an instance of a Version
of this Resource type, not the Resource itself.xid entity attribute that includes a target value as part of
its model definition MUST match the target entity type specified. An
xid attribute that does not include target definition has no
such restriction and MAY be any valid xid value.target as part of its
definition. If so, then any runtime value that is a relative URI/URL
(begins with /) MUST be an xid and MUST adhere to the target entity
type specified, if specified. Absolute URIs/URLs are not constrained by
the presence of a target value./endpoints/messagesattributes.<STRING>.namecharset 🔗Type: String.
OPTIONAL, and MUST only be used when type is object.
Specifies the name of the character set that defines the allowable characters that can be used for the object's top-level attribute names. Any attempt to define a top-level attribute for this object that does not adhere to the characters defined by the character set name MUST generate an error (invalid_attribute).
Per the Attributes and Extensions
section, attribute names are normally limited to just the set of characters
that ensure they can reliably be used in cases such as code variable names
without the need for some escaping mechanism. However, there are
situations where object-typed attribute names need to support additional
characters, such as a dash (-), and it is known that they will never be
used in those restricted character set situations. By setting the
namecharset aspect to extended the server MUST allow for an extended
set of valid characters in attribute names for this object.
The allowed character set for attribute names within an object MUST also
apply to the top-level siblingattributes of any ifvalues defined
for those attributes.
This specification defines two character sets:
strict - this character set is the same as the set of characters
defined for all attribute names - see Attributes and
Extensions.extended - this character set is the same as the set of characters
defined for all map key names - see Attributes and
Extensions.When not specified, the default value is strict.
The value of this attribute MUST be case-insensitive.
Implementations MAY define additional character sets, however, an attempt
to define a model that uses an unknown character set name MUST generate an
error (model_error). There is currently no
mechanism defined by this specification to discover the list (or
definition) of additional namecharset values supported by an
implementation. Implementations SHOULD use their documentation to
advertise this extension.
attributes.<STRING>.description 🔗attributes.<STRING>.enum 🔗Type: Array of values of type attributes.<STRING>.type..
OPTIONAL.
A list of possible values for this attribute. Each item in the array MUST
be of type defined by type. When not specified, or an empty array, there
are no restrictions on the value set of this attribute. This MUST only be
used when the type is a scalar. See the strict attribute below.
When specified without strict being true, this list is just a
suggested set of values and the attribute is NOT REQUIRED to use one of
them.
attributes.<STRING>.strict 🔗enum or not. A value of true means that any
values used that are not part of the enum set MUST generate an error
(invalid_attribute).
This attribute has no impact when enum is absent or an empty array.true.attributes.<STRING>.matchcase 🔗string attribute's value MUST be compared with
a matching value in a case-sensitive way, or not.true if the owning attribute's type (or
item.type for non-scalars) is not "string".false.attributes.<STRING>.readonly 🔗Type: Boolean.
OPTIONAL.
Indicates whether this attribute is modifiable by a client. During creation, or update, of an entity if this attribute is specified, then its value MUST be silently ignored by the server even if the value is invalid.
Typically, attributes that are completely under the server's control
will be readonly - e.g. self.
When not specified, the default value MUST be false.
When the attribute name is * then readonly MUST NOT be set to true.
attributes.<STRING>.immutable 🔗Type: Boolean.
OPTIONAL.
Indicates whether this attribute's value can be changed once it is set.
This MUST ONLY be used for server-controlled specification-defined
attributes, such as specversion and <SINGULAR>id, and MUST NOT be used
for extension attributes. As such, it is only for informational purposes
for clients.
Once set, any attempt to update the value MUST be silently ignored by the server.
When not specified, the default value MUST be false.
attributes.<STRING>.required 🔗true, this specification does not mandate how this
attribute's value is populated (i.e. by a client, the server or via a
default value), just that by the end of processing any request it MUST
have a non-null value, and generate an error
(invalid_attribute) if not.true value also implies that this attribute MUST be serialized in any
response from the server - with the exception of the optimizations
specified for document view.false.* then required MUST NOT be set to true.false if a default value (attributes.<STRING>.default)
is defined. If not true when default has a value, an error MUST be
generated (model_required_true).attributes.<STRING>.default 🔗null value of the type specified by the
attributes.<STRING>.type model attribute and MUST only be used for
scalar types. Attempts to define a default value for a non-scalar type
MUST generate an error
(model_scalar_default).null.required aspect set to true.null, which would reset it to the current default value) of existing,
instances of the attribute.attributes.<STRING>.attributes 🔗attributes above.type is object, otherwise it
MUST NOT be present. It MAY be absent or an empty list if there are no
defined attributes for the nested object.attributes.<STRING>.item 🔗type is map or array.type value is
map or array.attributes.<STRING>.item.type 🔗attributes.<STRING>.item.target 🔗item.type is url-reference,
uri-reference or xid.attributes.<STRING>.target above.attributes.<STRING>.item.namecharset 🔗namecharset above.item.type is object.attributes.<STRING>.item.attributes 🔗attributes above.item.type is object.attributes.<STRING>.item.item 🔗attributes.<STRING>.item above.item.type is map or array.attributes.<STRING>.ifvalues 🔗Type: Map where potential runtime values of the attribute are the keys of the map.
OPTIONAL.
This map can be used to conditionally include additional
attribute definitions based on the runtime value of the current attribute.
If the string serialization of the runtime value of this attribute matches
the ifvalues key (case-insensitive), then the siblingattributes MUST be
included in the model as siblings to this attribute.
While the properties of a map will automatically prevent two entries with the same value, they will not prevent two entries that only differ in case. Therefore, during a model update, servers MUST ensure that no two entries are the same irrespective of case, otherwise an error (model_error) MUST be generated.
If enum is not empty and strict is true then this map MUST NOT
contain any value that is not specified in the enum array.
This aspect MUST only be used for scalar attributes.
All attributes defined for this ifvalues MUST be unique within the scope
of this ifvalues and MUST NOT match a named attribute defined at this
level of the entity. If multiple ifvalues sections, at the same entity
level, are active at the same time then there MUST NOT be duplicate
ifvalues attributes names between those ifvalues sections.
ifvalues <STRING> MUST NOT be an empty string.
ifvalues <STRING> MUST NOT start with the ^ (caret) character as
its presence at the beginning of <STRING> is reserved for future use.
ifvalues siblingattributes MAY include additional ifvalues
definitions.
groups 🔗groups.plural) of the
Group type (<GROUPS>).groups.<STRING> 🔗groups.<STRING>.plural
for more information.groups.<STRING>.plural 🔗endpoints (<GROUPS>).groups.<STRING>.singular 🔗endpoint (<GROUP>).groups.<STRING>.description 🔗groups.<STRING>.icon 🔗icon for more information.groups.<STRING>.labels 🔗labels]((./spec.md#labels) above.groups.<STRING>.modelversion 🔗1.2groups.<STRING>.modelcompatiblewith 🔗https://xregistry.io/xreg/xregistryspecs/schema-v1/docs/model.jsongroups.<STRING>.attributes 🔗attributes above.groups.<STRING>.ximportresources 🔗groups.<STRING>.resources 🔗groups.resources.plural)
of the Resource type (<RESOURCES>).groups.<STRING>.resources.groups.<STRING>.resources.<STRING>.plural for more information.groups.<STRING>.resources.<STRING>.plural 🔗messages (<RESOURCES>).groups.<STRING>.resources.<STRING>.singular 🔗message (<RESOURCE>).groups.<STRING>.resources.<STRING>.description 🔗groups.<STRING>.resources.<STRING>.icon 🔗icon for more information.groups.<STRING>.resources.<STRING>.labels 🔗attributes above.groups.<STRING>.resources.<STRING>.modelversion 🔗modelversion above.groups.<STRING>.resources.<STRING>.modelcompatiblewith 🔗modelversion above.groups.<STRING>.resources.<STRING>.maxversions 🔗0).0) indicates there is no stated limit, and
implementations MAY prune non-default Versions at any time. This means
it is valid for an implementation to only support one (1) Version when
maxversions is set to 0.versionmode
algorithm), skipping the Version marked as "default".
Once the single oldest Version is determined, delete it.
A special case for the pruning rules is that if maxversions is set to
one (1), then the "default" Version is not skipped, which means it will be
deleted and the new Version will become "default".groups.<STRING>.resources.<STRING>.setversionid 🔗true or false, case-sensitive).versionid is supported.true.true indicates the client MAY specify the versionid of a
Version during its creation process.false indicates that the server MUST choose an appropriate
versionid value during creation of the Version.setversionid is false and
a versionid is provided then the server MUST generate an error
(versionid_not_allowed).groups.<STRING>.resources.<STRING>.setdefaultversionsticky 🔗true or false, case-sensitive).true.true indicates a client MAY select the default Version of
a Resource via one of the methods described in this specification rather
than the server always choosing the default Version.false indicates the server MUST choose which Version is the
default Version.defaultversionid attribute when this aspect is
false MUST generate an error
(setdefaultversionid_not_allowed).true if maxversions is one (1). An attempt
to set it to false MUST generate an error
(setdefaultversionsticky_false.groups.<STRING>.resources.<STRING>.hasdocument 🔗Type: Boolean (true or false, case-sensitive).
OPTIONAL.
Indicates whether or not each Resource of this type has a domain-specific
document associated with it. If false then the xRegistry metadata becomes
"the document". Meaning, a query to the Resource's URL will return the
xRegistry metadata and not a domain-specific document.
A value of true does not mean that these Resources are guaranteed to
have a non-empty document, and a query to the Resource MAY return an
empty document.
See Document Resources vs Metadata-Only Resources for more information.
When not specified, the default value MUST be true.
A value of true indicates that each Resource of this type MUST have a
separate document associated with it, even if it's empty.
groups.<STRING>.resources.<STRING>.versionmode 🔗ancestor attribute will be populated when not
explicitly set by a client.ancestor attribute of all Versions of a Resource accurately
represent the relationship of the Versions prior to the completion of
any operation. For example, when the createdat algorithm is used and
the createdat timestamp of a Version is modified, this might cause a
reordering of the Versions and the ancestor attributes might need to
be changed accordingly. Similarly, the defaultversionid of the
Resource might change if its defaultversionsticky attribute is false.manual.manual.versionmode algorithms:
manual
ancestor of another Version, then
finding the one with the newest createdat timestamp. If there is
more than one, then the one with the highest alphabetically
case-insensitive versionid value MUST be chosen.ancestor value that points to itself), then finding
the one with the oldest createdat timestamp. If there is more than
one, then the one with the lowest alphabetically case-insensitive
versionid MUST be chosen.ancestor value
provided MUST be sorted/processed by versionid (in case-insensitive
ascending order) and the ancestor value of each MUST be set to the
current "newest version" per the above semantics. Note that as
each new Version is created, it MUST become the "newest". If there
is no existing Version then the new Version becomes a root and its
ancestor value MUST be its versionid attribute value.ancestor value MUST be set to its
versionid value.createdat
createdat timestamp. If there is more than one, then the
one with the highest alphabetically case-insensitive versionid
value MUST be chosen.createdat timestamp. If there is more than one, then the
one with the lowest alphabetically case-insensitive versionid
value MUST be chosen. Note that this MUST also be the one and only
"root" Version.ancestor value of each Version MUST be
determined via examination of the createdat timestamp of each
Version and the Versions sorted in ascending order, where the first
one will be the "root" (oldest) Version and its ancestor value
MUST be its versionid. If there is more than one Version with the
same createdat timestamp then those MUST be ordered in ascending
case-insensitive ordered based on their versionid values.ancestor value MUST be determined by the "ancestor processing" logic
as stated above.versionmode is used, the singleversionroot aspect
MUST be set to true.modifiedat
createdat algorithm except that the
modifiedat attribute of each Version MUST be used instead of the
createdat attribute.semver
versionid
value per the Semantic Versioning
specification's "precedence" ordering rules.versionid
value per the Semantic Versioning
specification's "precedence" ordering rules. Note that this MUST also
be the one and only "root" Version.ancestor value of each Version MUST either
be its versionid value (if it it the oldest Version), or the
versionid of the next oldest Version per the
Semantic Versioning specification's
"precedence" ordering rules.ancestor value MUST be determined by the "ancestor processing" logic
as stated above.versionmode is used, the singleversionroot aspect
MUST be set to true.groups.<STRING>.resources.<STRING>.singleversionroot 🔗true or false, case-sensitive).ancestor attribute value being the same as its versionid
attribute.false.true indicates that only one Version of the Resource can
be a root, and the server MUST generate an error
(multiple_roots) if any request results in a
state where more than one Version of a Resource is a root of an ancestor
tree.versionmode value might influence
the permissible values of this aspect.singleversionroot Policy
Enforcement section of
the Primer for more information.groups.<STRING>.resources.<STRING>.validateformat 🔗true or false, case-sensitive).format
value.false.hasdocument model attribute
set to false, a Version with no domain-specific document even though
hasdocument is true, and a Version with an empty domain-specific
document as 3 different variants of "the domain-specific document is
empty (zero bytes in length)".hasdocument is true, and a Version uses the <RESOURCE>url
attribute to reference the document in an external datastore, the
resulting formatvalidated attribute on the
Version MUST be "false".true indicates that the server MUST generate an error
(format_violation) if any Version of a Resource
instance of this Resource type does not adhere to the format value of that
Version. An absent format value on a Version MUST be treated as a request
to disable both "format" and "compatibility" verification logic for that
Version.false indicates that the server MUST NOT perform any format
checking for Versions of Resources of this Resource type.groups.<STRING>.resources.<STRING>.validatecompatibility 🔗true or false, case-sensitive).meta.compatibility value.false.true indicates that the server MUST generate an error
compatibility_violation) if any Version
of a Resource instance of this Resource type does not adhere to the rules of
the meta.compatibility value for that Resource's format value.
See strictvalidation for
exceptions to this requirement.format value then compatibility verification
MUST NOT be done.true then its sibling validateformat
attribute MUST also be true.false indicates that the server MUST NOT perform any
compatibility checking for instances of this Resource type.false, but there is a desire to advertise
the external entity that has performed the validation, a label MAY be
added to the Resource's model or to the Resource instance itself with this
information.groups.<STRING>.resources.<STRING>.strictvalidation 🔗true or false, case-sensitive)meta.compatibility or Version
format values are to be treated as errors or ignored.validateformat or
validatecompatibility are true. Otherwise, this attribute's value is
ignored by the server.false.true indicates that:
format validation logic MUST generate an error
(format_violation) if the Version's format
is an unsupported value.compatibility validation logic MUST generate an error
(compatibility_violation) if the
Resource's meta.compatibility value is an unsupported value.false indicates that:
format value is absent, then format and compatibility
validation logic MUST NOT be performed for that Version.format value is not supported, then the format
validation logic MUST NOT generate an error. Instead, the Version's
formatvalidated and compatibilityvalidated attributes MUST be set to
false.meta.compatibility value is unsupported, then
the Version's compatibilityvalidated attribute MUST be set to false.groups.<STRING>.resources.<STRING>.consistentformat 🔗true or false, case-sensitive)format value.validateformat
and validatecompatibility.false.true indicates that:
format attribute of all Versions of a Resource of this type MUST
have the same case-insensitive value (including the case of format being
an empty string). If a Version differs then an error
(format_inconsistent) MUST be generated.false indicates that:
format value across
the Versions of a Resource of this type.groups.<STRING>.resources.<STRING>.typemap 🔗Type: Map where the keys and values MUST be non-empty strings. The key
MAY include at most one * to act as a wildcard to mean zero or more
instances of any character at that position in the string - similar to a
.* in a regular expression. The key MUST be a case-insensitive string.
OPTIONAL.
When a Resource's metadata is serialized in a response and the
?inline=<RESOURCE> feature is enabled, the server will attempt to
serialize the Resource's "document" under the <RESOURCE> attribute.
However, this can only happen under two situations:
1 - The Resource document's bytes are already in the same format as
the xRegistry metadata - in other words JSON, or
2 - The Resource's document can be considered a "string" and therefore
can be serialized as a "string", possibly with some escaping.
For some well-known contenttype values (e.g. application/json), the
first case can be easily determined by the server. However, for custom
contenttype values the server will need to be explicitly told how to
interpret its value (e.g. to know if it is a string or JSON).
The typemap attribute allows for this by defining a mapping of
contenttype values to well-known xRegistry format types.
Since the contenttype value is a "media-type" per
RFC9110,
for purposes of looking it up in the typemap, just the type/subtype
portion of the value (case-insensitively) MUST be used. Meaning, any
parameters MUST be excluded.
If more than one entry in the typemap matches the contenttype, but
they all have the same value, then that value MUST be used. If they are
not all the same, then binary MUST be used.
This specification defines the following values (case-insensitive):
binaryjsonstringImplementations MAY define additional values.
A value of binary indicates that the Resource's document is to be treated
as an array of bytes and serialized under the <RESOURCE>base64 attribute,
even if the contenttype is of the same type of the xRegistry metadata
(e.g. application/json). This is useful when it is desirable to not
have the server potentially modify the document (e.g. "pretty-print" it).
A value of json indicates that the Resource's document is JSON and MUST
be serialized under the <RESOURCE> attribute if it is valid JSON. Note
that if there is a syntax error in the JSON then the server MUST treat the
document as binary to avoid sending invalid JSON to the client. The
server MAY choose to modify the formatting of the document (e.g. to
"pretty-print" it).
A value of string indicates that the Resource's document is to be treated
as a string and serialized using the default string serialization rules
for the format being used to serialize the Resource's metadata. For
example, when using JSON, this means escaping all non-printable characters.
Specifying an unknown (or unsupported) value MUST generate an error (model_error) during the update of the xRegistry model.
By default, the following
RFC9110
typemap keys MUST be implicitly defined as follows, unless overridden
by an explicit typemap entry:
application/json: mapped to json*+json: mapped to jsontext/plain: mapped to stringExample:
"typemap": {
"text/*": "string",
"text/mine": "json"
}
groups.<STRING>.resources.<STRING>.attributes 🔗attributes above,
as well as
resourceattributes
and metaattributes
below.groups.resources.resourceattributes level.
The only duplicate names allowed are specification-defined attributes
such as self and xid, and the Version-specific values MUST be
overridden by the Resource-level values when serialized.groups.<STRING>.resources.<STRING>.resourceattributes 🔗attributes above.metaurl, that exist to help in the navigation of the entities. Users
MUST NOT define additional attributes for this list. Extension
Resource-level attributes would appear in the metaattributes list, while
Version-level extensions would appear in the attributes list.groups.<STRING>.resources.<STRING>.metaattributes 🔗attributes above.meta sub-object of the Resource.Clarifying the usage of the attributes, resourceattributes and
metaattributes:
defaultversionid), is serialized under the meta sub-object. This avoids
potential name conflicts between Version and Resource-level attributes, as
well as avoiding making the serialization of the Resource too verbose/noisy.meta sub-object, would appear to be misplaced. For example, the versions
collection attributes could be confusing to users since meta is not
the direct parent/owner of the "versions" collection, the Resource is.
Especially when considering the URL path to the "versions" collection would
not have /meta/ in it.self) need to appear on both
Resources as well as Versions but the values need to be different in each
case. This is why the same attribute names can appear both the
resourceattributes and attributes lists, but only specification-defined
attributes are allowed to have this naming conflict. Extensions are not, as
that could lead to confusion for users.attributes in order to make user creation of the model easier, leaving
the edge cases of Resource or Meta-level extension attributes to use more
verbosely named lists.The Registry model is available in two forms:
The full "model" view can be thought of as a full schema definition of what the message exchanges with the server might look like. As such, it MUST include:
<COLLECTION>* attributes. While the model
and modelsource attributes MUST appear, their definitions MAY be shallow -
meaning, they can be defined as just object with one attribute (*) of
type "any".<COLLECTION>* attributes.<COLLECTION>*, meta and
metaurl attributes. Note that the <RESOURCE>* attribute would only appear
if the
hasdocument aspect
aspect is true.The "modelsource" view of the model is just what was provided by the user when the model was defined, or last edited. It is expected that this view of the model is much smaller than the full model and only includes domain-specific information. While specification-defined attributes MAY appear in this document, they are NOT RECOMMENDED since the server will automatically add them so users do not need to concern themselves with those details.
The modelsource document is always a semantic subset of the full model document.
xRegistry protocol binding specifications will typically define a way to retrieve these two model views as both stand-alone entities and inlined as part of the Registry entity.
For the sake of brevity, this specification doesn't include the full definition of the specification-defined attributes as part of the snippets of output. However, an example of a full model definition of a Registry can be can be found in this sample sample-model-full.json.
When retrieving the modelsource, the response MUST only include what
was specified in the request to define the model - it MUST NOT include
any auto-added specification defined metadata that will appear under model.
A server MAY support updating the model of a Registry via:
modelsource entity, if the protocol binding
supports exposing it as a stand-alone entity.modelsource attribute
on a request to update the Registry entity.To enable support for a wide range of use cases, but to also ensure interoperability across implementations, the following rules have been defined with respect to how models are defined or updated:
type from string
to integer is not.required MUST NOT have this
aspect changed to false.readonly MUST NOT have this
aspect changed to false.Any specification attributes not included in a request to define, or update,
a model MUST be included in the resulting full model. In other words, the full
Registry's model consists of the specification-defined attributes overlaid
with the attributes that are explicitly-defined as part of a modelsource
update request.
Note: there is no mechanism defined to delete specification-defined attributes from the model.
Registries MAY support extension attributes to the model language (meaning, new attributes within the model definitions themselves), but only if the server supports them. Servers MUST generate an error (model_error) if a model definition includes unknown model language attributes.
Once a Registry has been created, changes to the model MAY be supported by server implementations. This specification makes no statement as to what types of changes are allowed beyond the following requirements:
Any request to update the model that does not adhere to those requirements MUST generate an error (model_compliance_error).
How the server guarantees that all entities in the Registry are compliant with the model is an implementation detail. For example, while it is NOT RECOMMENDED, it is valid for an implementation to modify (or even delete) existing entities to ensure model compliance. Instead, it is RECOMMENDED that the model update requests generate an error (model_compliance_error) if existing entities are not compliant.
For the purposes of validating that the existing entities in the Registry are
compliant with the model, the mechanisms used to define the model (e.g.
$include vs ximportresources vs defined locally) MUST NOT impact that
analysis. In other words, model updates that have no semantic changes but
rather switch between one of those 3 mechanisms MUST NOT invalidate any
existing entities in the Registry.
Additionally, it is STRONGLY RECOMMENDED that model updates be limited to backwards compatible changes.
Implementations MAY choose to limit the types of changes made to the model, or not support model updates at all.
The xRegistry model definition used to create a sample xRegistry can be found here, while the resulting "full" model (with all of the system-defined aspects added) can be found here.
When a Resource type definition is to be shared between Groups, rather than
creating a duplicate Resource definition, the ximportresources mechanism MAY
be used instead. The ximportresources attribute on a Group definition
allows for a list of <XIDTYPE> references to other Resource types that are
to be included within this Group.
For example, the following abbreviated model definition defines
one Resource type (messages) under the messagegroups Group, that is
also used by the endpoints Group.
"modelsource": {
"groups": {
"messagegroups": {
"singular": "messagegroup",
"resources": {
"messages": {
"singular": "message"
}
}
},
"endpoints": {
"singular": "endpoint",
"ximportresources": [ "/messagegroups/messages" ]
}
}
}
The format of the ximportresources specification is:
"ximportresources": [ "<XIDTYPE>", * ]
where:
<XIDTYPE> reference to another Group/Resource
combination defined within the same Registry. It MUST NOT reference the
same Group under which the ximportresources resides.ximportresources directive
that references a Resource from another Group that itself is defined
via an ximportresources. However, transitive definitions of Resources
MUST NOT result in a circular import chain.Locally defined Resources MAY be defined within a Group that uses the
ximportresources feature, however, Resource plural and singular values
MUST be unique across all imported and locally defined Resources.
See Cross Referencing Resources for more additional information.
There might be times when it is necessary for an xRegistry model to reuse portions of another xRegistry model defined elsewhere. Rather than forcing the duplication of the model definitions, an "include" type of JSON directive MAY be used.
The general formats of the include are:
"$include": "<PATH-TO-DOCUMENT>#<JSON-POINTER-IN-DOC>"
or
"$includes": [ "<PATH-TO-DOCUMENT>#<JSON-POINTER-IN-DOC>" * ]
where the first form specifies a single reference to be included, and the
second form specifies multiple. The fragment (#...) portion is OPTIONAL.
For example:
"$include": "http://example.com/xreg-model.json#/groups/mygroup/attributes"
is asking for the attributes of a Group called mygroup to be included at
this location of the current model definition.
These directives MAY be used in any JSON Object or Map entity in an xRegistry model definition. The following rules apply for how to process the include directive:
$include attribute.name of the attributes.$includes is used, the references MUST be processed in order and
earlier attributes included take precedence over subsequently included
attributes.$include and $includes MUST NOT be present at the same time at the
same level in the model.include directives, but MUST NOT be
recursive.#...) part of the reference MUST adhere to the
JSON Pointer specification.When the directives are used in a request to update the model, the server MUST
resolve all includes prior to updating the model. The original (source)
model definition, with any "include" directives, MUST be available via
the modelsource attribute/entity. The expanded model (after the resolution
of any includes, and after all specification-defined attributes have been
added), MUST be available via the model attribute/entity. The directives MUST
only be processed during the initial update of the model. In order to have
them re-evaluated, a subsequent model update request (with those directive)
MUST be sent.
When there is tooling used outside of the server, e.g. in an xRegistry
client, if that tooling resolves the "include" directives prior to sending
the model to the server, then the directives will not appear in the
modelsource view of the the model. Ideally, tooling SHOULD allow users
to choose whether the resolution of the directives are done locally or by
the server.
Examples:
A model definition that includes xRegistry attributes from a file on a remote
server, and adds the definition of one attribute to a Group named mygroups
from an external Group named group1 in another xRegistry.
{
"attributes": {
"$include": "http://example.com/someattributes",
"myattribute": {
"name": "myattribute",
"type": "string"
}
}
"groups": {
"mygroups": {
"singular": "mygroup",
"attributes": {
"attr1": {
"$include": "http://example.com/model#/groups/group1/attributes/attr1"
}
... remainder of model excluded for brevity ...
}
}
}
}
where http://example.com/someattributes might look like:
{
"myattr": {
"name": "myattr",
"type": "string"
}
}
and the second include target might look like:
{
"name": "attr1",
"type": "string"
}