A Registry Service exposes Resources and their metadata, for the purpose of enabling discovery of those Resources for either end-user consumption or automation and tooling.
A Registry Service is one that manages metadata about Resources. At its core, the management of an individual Resource is simply a REST-based interface for creating, modifying, and deleting the Resource. However, many Resource models share a common pattern of grouping Resources and can optionally support versioning of those Resources. This specification aims to provide a common interaction pattern for these types of services with the goal of providing an interoperable framework that will enable common tooling and automation to be created.
This document is meant to be a framework from which additional specifications can be defined that expose model-specific Resources and metadata.
As of today, this specification only specifies an HTTP-based interaction model. This is not meant to imply that other protocols cannot be supported, and other protocols will likely be added in the future. When that happens, this specification will be restructured to have clean separation between a protocol-agnostic core and protocol-specific requirements.
A Registry consists of two main types of entities: Groups and Resources.
Groups, as the name implies, is a mechanism by which related Resources are arranged together under a single collection - the Group. The reason for the grouping is not defined by this specification, so the owners of the Registry can choose to define (or enforce) any pattern they wish. In this sense, a Group is similar to a "directory" on a filesystem.
Resources represent the main data of interest for the Registry. In the filesystem analogy, these would be the "files". All Resources exist under a single Group and, similar to Groups, have a set of Registry metadata. However, unlike a Group which only has Registry metadata, each Resource can also have a "document" associated with it. For example, a "schema" Resource might have a "schema document" as its "document". This specification places no restriction on the type of content stored in the Resource's document.
This specification defines a set of common metadata that can appear on both Groups and Resources, and allows for domain-specific extensions to be added.
The following 3 diagrams show (from left to right):
1 - The core concepts of the Registry in its most abstract form.
2 - A Registry concept model with multiple types of Groups/Resources.
3 - A concrete sample usage of Registry that includes the use of an attribute
on "Message Definition" that is a reference to a "Schema" document - all
within the same Registry instance.
For easy reference, the JSON serialization of a Registry adheres to this form:
{
"specversion": "STRING",
"registryid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"capabilities": { # Supported capabilities/options
"flags": [ # Query parameters
"collections",? "doc",? "epoch",? "filter",? "inline",?
"nodefaultversionid",? "nodefaultversionsticky",? "noepoch",?
"noreadonly",? "offered",? "schema",? "setdefaultversionid",?
"specversion",?
"STRING" *
],
"mutable": [ # What is mutable in the Registry
"capabilities",? "entities",? "model",? "STRING"*
], ?
"pagination": BOOLEAN, ?
"schemas": [ "xRegistry-json/1.0-rc1", "STRING" * ], ?
"shortself": BOOLEAN, ?
"specversions": [ "1.0-rc1", "STRING"* ], ?
"sticky": BOOLEAN, ?
"STRING": ... * # Extension capabilities
}, ?
"model": { # Only if inlined
"labels": { "STRING": "STRING" * }, ?
"attributes": { # Registry level attributes/extensions
"STRING": { # Attribute name (case-sensitive)
"name": "STRING", # Same as attribute's key
"type": "TYPE", # string, decimal, array, object, ...
"target": "STRING", ? # If "type" is "xid"
"namecharset": "STRING", ? # If "type" is "object"
"description": "STRING", ?
"enum": [ VALUE * ], ? # Array of scalar values of type "TYPE"
"strict": BOOLEAN, ? # Just "enum" values? Default=true
"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
"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
"VALUE": { # Possible attribute value
"siblingattributes": { ... } # See "attributes" above
} *
} ?
} *
},
"groups": {
"STRING": { # Key=plural name, e.g. "endpoints"
"plural": "STRING", # e.g. "endpoints"
"singular": "STRING", # e.g. "endpoint"
"description": "STRING", ?
"modelversion": "STRING", ? # Version of the group model
"compatiblewith": "URI", ? # Statement of compatibility with model spec
"labels": { "STRING": "STRING" * }, ?
"attributes": { ... }, ? # Group level attributes/extensions
"resources": {
"STRING": { # Key=plural name, e.g. "messages"
"plural": "STRING", # e.g. "messages"
"singular": "STRING", # e.g. "message"
"description": "STRING", ?
"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
"singleversionroot": BOOLEAN, ? # Default=false"
"typemap": MAP, ? # contenttype mappings
"modelversion": "STRING", ? # Version of the resource model
"compatiblewith": "URI", ? # Statement of compatibility with model spec
"labels": { "STRING": "STRING" * }, ?
"attributes": { ... }, ? # Version attributes/extensions
"metaattributes": { ... } ? # Resource attributes/extensions
} *
} ?
} *
} ?
}, ?
# Repeat for each Group type
"GROUPSurl": "URL", # e.g. "endpointsurl"
"GROUPScount": UINTEGER, # e.g. "endpointscount"
"GROUPS": { # Only if inlined
"KEY": { # Key=the Group id
"GROUPid": "STRING", # The Group ID
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
# Repeat for each Resource type in the Group
"RESOURCESurl": "URL", # e.g. "messagesurl"
"RESOURCEScount": UINTEGER, # e.g. "messagescount"
"RESOURCES": { # Only if inlined
"KEY": { # The Resource id
"RESOURCEid": "STRING",
"versionid": "STRING", # Default Version's ID
"self": "URL", # Resource URL, not Version
"shortself": "URL", ?
"xid": "XID", # Resource XID, not Version
"epoch": UINTEGER, # Start of default Ver attrs
"name": "STRING", ?
"isdefault": true,
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"ancestor": "STRING",
"contenttype": "STRING, ? # Add default Ver extensions
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If local & inlined & JSON
"RESOURCEbase64": "STRING", ? # If local & inlined & ~JSON
# End of default Ver attrs
# Resource level helper attributes
"metaurl": "URL",
"meta": { # Only if inlined
"RESOURCEid": "STRING",
"self": "URL", # URL to "meta" object
"shortself": "URL", ?
"xid": "XID",
"xref": "URL", ? # xid of linked Resource
"epoch": UINTEGER, # Resource's epoch
"createdat": "TIMESTAMP", # Resource's
"modifiedat": "TIMESTAMP", # Resource's
"readonly": BOOLEAN, # Default=false
"compatibility": "STRING", # Default=none
"compatibilityauthority": "STRING", ? # Default=external
"deprecated": {
"effective": "TIMESTAMP", ?
"removal": "TIMESTAMP", ?
"alternative": "URL", ?
"docs": "URL"?
}, ?
"defaultversionid": "STRING",
"defaultversionurl": "URL",
"defaultversionsticky": BOOLEAN # Default=false
}, ?
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { # Only if inlined
"KEY": { # The Version's versionid
"RESOURCEid": "STRING", # The Resource id
"versionid": "STRING", # The Version id
"self": "URL", # Version URL
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER, # Version's epoch
"name": "STRING", ?
"isdefault": BOOLEAN, # Default=false
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"ancestor": "STRING", # Ancestor's versionid
"contenttype": "STRING", ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If inlined & JSON
"RESOURCEbase64": "STRING" ? # If inlined & ~JSON
} *
} ?
} *
} ?
} *
} ?
}
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
For clarity, OPTIONAL attributes (specification-defined and extensions) are OPTIONAL for clients to use, but the servers' responsibility will vary. Server-unknown extension attributes MUST be silently stored in the backing datastore. Specification-defined, and server-known extension attributes MUST generate an error if the corresponding feature is not supported or enabled. However, as with all attributes, if accepting the attribute results in a bad state (such as exceeding a size limit, or results in a security issue), then the server MAY choose to reject the request.
In the pseudo JSON format snippets ?
means the preceding item is OPTIONAL,
*
means the preceding item MAY appear zero or more times, and +
means the
preceding item MUST appear at least once. The presence of the #
character
means the remaining portion of the line is a comment. Whitespace characters in
the JSON snippets are used for readability and are not normative.
When HTTP query parameters are discussed, they are presented as ?NAME
where
NAME
is the name of the query parameter.
Use of the words GROUP
and RESOURCE
are meant to represent the singular
name of a Group and Resource type being used. While GROUPS
and RESOURCES
are the plural name of those respective types. Use of the word SINGULAR
represents the singular name of the entity being referenced. For example,
for a "schema document" Resource type where its plural name is defined as
schemas
and its singular name is defined as schema
, the SINGULAR
value would be schema
.
Use of acronyms and words in all capital letters (e.g. KEY
) typically
represent a field that will be replaced by its real value at runtime.
Additionally, the following acronyms are defined:
gID
is the SINGULARid
of a single Group.rID
is the SINGULARid
of a single Resource.vID
is the versionid
of a single Version of a Resource.The following are used to denote an instance of one of the associated data types (see Attributes and Extensions for more information about each data type):
ARRAY
BOOLEAN
DECIMAL
INTEGER
MAP
OBJECT
STRING
TIMESTAMP
UINTEGER
URI
URIABSOLUTE
URIRELATIVE
URITEMPLATE
URL
URLABSOLUTE
URLRELATIVE
XID
TYPE
- one of the allowable data type names (MUST be in lower case) listed
in Attributes and ExtensionsThis specification defines the following terms:
An entity that acts as a collection of related Resources.
An implementation of this specification. Typically, the implementation would include model-specific Groups, Resources and extension attributes.
A Resource is typically the main entity that is stored within a Registry Service. A Resource MUST exist within the scope of a Group and it MAY be versioned.
This section defines common Registry metadata attributes and APIs. It is an explicit goal for this specification that metadata can be created and managed in files in a file system, for instance in a Git repository, and also managed in a Registry service that implements the API described here.
For instance, during development of a module, the metadata about the events raised by the modules will best be managed in a file that resides alongside the module's source code. When the module is ready to be deployed into a concrete system, the metadata about the events will be registered in a Registry service along with the endpoints where those events can be subscribed to or consumed from, and which allows discovery of the endpoints and all related metadata by other systems at runtime.
Therefore, the hierarchical structure of the Registry model is defined in such a way that it can be represented in a single file, including but not limited to JSON, or via the entity graph of a REST API.
If the processing of a request fails then an error MUST be generated and the entire request MUST be undone. See the Error Processing section for more information.
In the remainder of this specification, in particular when defining the attributes of the Registry entities, the terms "document view" or "API view" will be used to indicate whether the serialization of the entity in a response is meant for use as a stand-alone document or as part of a REST API message exchange. The most notable differences are that in document view:
GET
to retrieve it.versions
collection will most likely
include the "default" Version, so duplicating that information is redundant.Most of these differences are to make it easier for tooling to use the "stand-alone" document view of the Registry. For a complete list of the differences in "document view" see the Doc Flag flag and the Exporting section.
Note that "document view" only refers to response messages. There is no "document view" concept for requests. However, "document view" responses are designed such that they can be used in request messages as they still convey the same information as an "API view" response.
This specification only defines the core APIs, and their semantics, of a
Registry service. It does not address many of the details that would need to
be added for a live instance of a service; as often times these aspects are
very specific to the environment in which the service is running. For example,
this specification does not address authentication or authorization levels of
users, nor how to securely protect the APIs (aside from the implied use of
https
), clients or servers from attacks. Implementations of this
specification are expected to add these various features as needed.
Additionally, implementation MAY choose to customize the data and behavior on a per-user basis as needed. For example, the following customizations might be implemented:
readonly
Resource. Note that in this case the Resource's readonly
aspect will likely appear with a value of true
even for the admin.The goal of these customizations is not to allow for implementation to violate the specification, rather it is to allow for real-world requirements to be met while maintaining the interoperability goals of the specification.
Implementations are encouraged to contact the xRegistry community if it is unclear if certain customizations would violate the specification.
Unless otherwise noted, all attributes and extensions MUST be mutable and MUST be one of the following data types:
any
- an attribute of this type is one whose type is not known in advance
and MUST be one of the concrete types listed here.array
- an ordered list of values that are all of the same data type - one
of the types listed here.
null
type of value to
appear in an array (e.g. [ null, 2, 3 ]
in an array of integers). In
these cases, while it is valid for the serialization being used, it is
not valid for xRegistry since null
is not a valid integer
. Meaning,
the serialization of an array that is syntactically valid for the
format being used, but not semantically valid per the xRegistry model
definition MUST NOT be accepted and MUST generate an error
(invalid_data_type).boolean
- case-sensitive true
or false
.decimal
- number (integer or floating point).integer
- signed integer.map
- set of key/value pairs, where the key MUST be of type string. The
value MUST be of one of the types defined here.
[a-z0-9]
), :
, -
, _
or a .
; be no longer
than 63 characters; start with an alphanumeric character and be unique
within the scope of this map.object
- a nested entity made up of a set of attributes of these data types.xid
- MUST be a URL
(xid) reference to another entity defined within
the Registry. The actual entity attribute value MAY reference a non-existing
entity (i.e. dangling pointer), but the syntax MUST reference a
defined/valid type in the Registry. This type of attribute is used in
place of url
so that the Registry can do "type checking" to ensure the
value references the correct type of Registry entity. See the definition of
the target
model attribute for more information.string
- sequence of Unicode characters.timestamp
- an RFC3339 timestamp.uinteger
- unsigned integer.uri
- a URI as defined in RFC 3986.
Note that it can be absolute or relative.uriabsolute
- absolute URI as defined in RFC 3986 Section
4.3.urirelative
- relative URI as defined in RFC 3986 Section
4.2.uritemplate
- URI Template as defined in
RFC 6570 Section 3.2.1.url
- an absolute URL (urlabsolute
) or relative URL (urlrelative
).urlabsolute
- an absolute URI as defined in RFC 3986 Section
4.3 with the
added "URL" constraints mentioned in RFC 3986 Section
1.1.3.urlrelative
- a relative URI as defined in RFC 3986 Section
4.2 with the
added "URL" constraints mentioned in RFC 3986 Section
1.1.3.The 6 variants of URI/URL are provided to allow for strict type adherence
when needed. However, for attributes that are simply "pointers" that might
in practice be any of those 6 types it is RECOMMENDED that uri
be used.
Attributes that are defined to be relative URIs or URLs MUST state what they are relative to and any constraints on their values, if any.
The root path of a Registry service MAY be at the root of a host or have a
PATH
portion in its URL (e.g. http://example.com/myregistry
).
The "scalar" data types are: boolean
, decimal
, integer
, string
,
timestamp
, uinteger
, uri
, uriabsolute
, urirelative
, uritemplate
,
url
, urlabsolute
, urlrelative
, xid
.
Note that any
is not a "scalar" type as its runtime value could be a complex
type such as object
.
All attributes (specification-defined and extensions) MUST adhere to the following rules:
[a-z0-9_]
) and MUST NOT start with a digit ([0-9]
).documentation
can be considered such an attribute
for description
.any
type) but a concrete type is needed to successfully process it, then the
server SHOULD default it to type string
. For example, if an extension is
defined as a map whose values are of type any
, but it appears in an HTTP
header with a value of 5
(and it is not clear if this would be an integer
or a string), if the server needs to convert this to a concrete data type,
then string
is the default choice.any
type has been used higher-up in the model. As a result, any portion of
the entity that appears under the scope of an any
typed attribute or
map-value is NOT REQUIRED to be validated except to ensure that the syntax
is valid per the rules of the serialization format used.null
or not being present
at all, and for the sake of brevity, SHOULD NOT be serialized as part of its
owning entity in server responses. Likewise, specifying them with a value of
null
in client requests SHOULD be reserved for cases where the client
needs to indicate a request to delete an attribute value (null
in the
request) rather than to leave the attribute untouched (absent in the
request), such as when PATCH
is used.Implementations of this specification MAY define additional (extension) attributes. However, they MUST adhere to the following rules:
*
(undefined) extension attribute
name at that level in the model.any
type for one of its parent
attribute definitions.RESOURCE*
and COLLECTION*
attributes that
are implicitly defined. Note that if a Resource type has the hasdocument
attribute set the false
then this rule does not apply for the RESOURCE*
attributes as those attributes are not used for that Resource type.The following attributes are used by one or more entities defined by this specification. They are defined here once rather than repeating them throughout the specification.
For easy reference, the JSON serialization of these attributes adheres to this form:
"SINGULARid": "STRING"
"self": "URL"
"shortself": "URL"
"xid": "XID"
"epoch": UINTEGER
"name": "STRING"
"description": "STRING"
"documentation": "URL"
"labels": { "STRING": "STRING" * }
"createdat": "TIMESTAMP"
"modifiedat": "TIMESTAMP"
The definition of each attribute is defined below:
SINGULARid
(id
) AttributeType: String
Description: An immutable unique identifier of the Registry, Group, Resource
or Version. The actual name of this attribute will vary based on the entity
it identifies. For example, a schema
Resource would use an attribute name
of schemaid
. This attribute MUST be named registryid
for the Registry
itself, and MUST be named versionid
for all Version entities.
Constraints:
unreserved
characters
(ALPHA / DIGIT / -
/ .
/ _
/ ~
) and @
, MUST start with
ALPHA, DIGIT or _
and MUST be between 1 and 128 characters in length.SINGULARid
MUST be treated as "not found".SINGULARid
is specified outside of the
serialization of the entity (e.g. part of a request URL, or a map key), its
presence within the serialization of the entity is OPTIONAL. However, if
present, it MUST be the same as any other specification of the SINGULARid
outside of the entity, and it MUST be the same as the entity's existing
SINGULARid
if one exists, otherwise an error
(mismatched_id) MUST be generated.Examples:
a183e0a9-abf8-4763-99bc-e6b7fcc9544b
myEntity
myEntity.example.com
While SINGULARid
can be something like a UUID, when possible, it is
RECOMMENDED that it be something human friendly as these values will often
appear in user-facing situations such as URLs or as command-line parameters.
And, in cases where name
is absent, it might be used as
the display name.
Note, since SINGULARid
is immutable, in order to change its value, a new
entity would need to be created with the new SINGULARid
that is a deep-copy
of the existing entity. Then the existing entity would be deleted.
self
AttributeType: URL
Description: A server-generated unique URL referencing the current entity.
Each entity in the Registry MUST have a unique self
URL value that
locates the entity in the Registry hierarchy and from where the entity can
be retrieved.
When specified as an absolute URL, it MUST be based on the URL of the Registry root appended with the hierarchy path of the Registry entities/collections leading to the entity.
In the case of pointing to an entity that has a SINGULARid
attribute,
the URL MUST be a combination of the URL used to retrieve its parent
appended with its SINGULARid
value.
API View Constraints:
hasdocument
aspect is set
to true
, then this URL MUST include the $details
suffix to its
SINGULARid
if it is serialized in the HTTP body response. If the aspect
is set to false
, then this URL's SINGULARid
MUST NOT include it.Document View Constraints:
#JSON-POINTER
where the JSON-POINTER
locates this entity within the current document. See Doc Flag
for more information.$details
suffix on its SINGULARid
.Examples:
https://example.com/registry/endpoints/ep1
(API View)#/endpoints/ep1
(Document View)shortself
AttributeType: URL
Description: A server generated unique absolute URL for an entity. This
attribute MUST be an alternative URL for the owning entity's self
attribute. The intention is that shortself
SHOULD be shorter in length
than self
such that it MAY be used when the length of the URL referencing
the owning entity is important. For example, in cases where the size of a
message needs to be as small as possible.
This specification makes no statement as to how this URL is constructed,
to which host/path it references, or whether a request to this URL
will directly perform the desired operation or whether it returns a
redirect to the full self
URL requiring the client to resend the request.
If an entity is deleted and then a new entity is created that results in
the same self
URL, this specification does not mandate that the same
shorturl
be generated, but it MAY do so.
This attribute MUST only appear in the serialization if the shortself
capability is enabled. However, if this capability is enabled, then disabled,
and then re-enabled, the shortself
values MUST retain their original
values. In this sense, implementations might create a shortself
that is
known for the lifetime of the entity and the capability controls whether
the attribute is serialized or not.
Constraints:
shortself
capability is enabled.shortself
capability is disabled.self
URL, either directly or indirectly via an HTTP redirect.Examples:
https://tinyurl.com/xreg123
redirects to
https://example.com/endpoints/e1
xid
AttributeType: XID
Description: An immutable server generated unique identifier of the entity.
Unlike SINGULARid
, which is unique within the scope of its parent, xid
MUST be unique across the entire Registry, and as such is defined to be a
relative URL from the root of the Registry. This value MUST be the same as
the PATH portion of its self
URL, after the Registry's base URL, without
any $
suffix (e.g. $details
). Unlike some other relative URIs, xid
values MUST NOT be shortened based on the incoming request's URL. xid
s
are always relative to the root path of the Registry.
This attribute is provided as a convenience for users who need a reference
to the entity without running the risk of incorrectly extracting it from
the self
URL, which might be ambiguous at times. The xid
value is also
meant to be used as a xref
value (see Cross Referencing
Resources, or as the value for attributes of
type xid
(see target
model attribute).
Constraints:
/[GROUPS/gID[/RESOURCES/rID[/meta | /versions/vID]]]
./
character.Examples:
/endpoints/ep1
epoch
AttributeType: Unsigned Integer
Description: A numeric value used to determine whether an entity has been
modified. Each time the associated entity is updated, this value MUST be
set to a new value that is greater than the current one. This attribute
MUST be updated for every update operation, even if no attributes were
explicitly updated, such as a PATCH
with no attributes. This then acts
like a touch
type of operation.
During a single write operation, whether this value is incremented for each modified attribute of the entity, or updated just once for the entire operation is an implementation choice.
During a create operation, if this attribute is present in the request, then it MUST be silently ignored by the server.
During an update operation, if this attribute is present in the request, then
an error (mismatched_epoch) MUST be generated if the
request includes a non-null value that differs from the existing value. A
value of null
MUST be treated the same as a request with no epoch
attribute at all.
If an entity has a nested xRegistry collection, its epoch
value MUST
be updated each time an entity in that collection is added or removed.
However, its epoch
value MUST NOT be updated solely due to modifications of
an existing entity in the collection.
Constraints:
Examples:
0
, 1
, 2
, 3
name
AttributeType: String
Description: A human-readable name of the entity. This is often used
as the "display name" for an entity rather than the SINGULARid
especially
when the SINGULARid
might be something that isn't human friendly, like a
UUID. In cases where name
is absent, the SINGULARid
value SHOULD be
displayed in its place.
This specification places no uniqueness constraints on this attribute.
This means that two sibling entities MAY have the same value. Therefore,
this value MUST NOT be used for unique identification purposes, the
SINGULARid
MUST be used instead.
Note that implementations MAY choose to enforce additional constraints on
this value. For example, they could mandate that SINGULARid
and name
be
the same value. Or, it could mandate that name
be unique within the scope
of a parent entity. How any such requirement is shared with all parties is
out of scope of this specification.
Constraints:
Examples:
My Cool Endpoint
description
AttributeType: String
Description: A human-readable summary of the purpose of the entity.
Constraints:
Examples:
A queue of the sensor generated messages
documentation
AttributeType: URL
Description: A URL to additional information about this entity.
This specification does not place any constraints on the data returned from
an HTTP GET
to this URL.
Constraints:
GET
to this URL.Examples:
https://example.com/docs/myQueue
labels
AttributeType: Map of name/value string pairs
Description: A mechanism in which additional metadata about the entity can be stored without changing the schema of the entity.
Constraints:
Examples:
"labels": { "owner": "John", "verified": "" }
when in the HTTP bodyxRegistry-labels-owner: John
xRegistry-labels-verified:
when in HTTP headersNote: HTTP header values can be empty strings but some client-side tooling
might make it challenging to produce them. For example, curl
requires
the header to be specified as -HxRegistry-labels-verified;
- notice the
semicolon(;
) is used instead of colon(:
). So, this might be something
to consider when choosing to use labels that can be empty strings.
createdat
AttributeType: Timestamp
Description: The date/time of when the entity was created.
Constraints:
modifiedat
value or the current
date/time. Implementations MAY choose to restrict its values if necessary.null
MUST use the current date/time
as the new value.defaultversionsticky
attribute is false
, then this Version MUST become the "default" Version.createdat
or modifiedat
attributes set to the current date/time due
to the processing rules above, MUST use the same value in all cases.Examples:
2030-12-19T06:00:00Z
modifiedat
AttributeType: Timestamp
Description: The date/time of when the entity was last updated
Constraints:
createdat
value or the current
date/time. Implementations MAY choose restrict its values if necessary.PATCH
with no attributes provided), MUST update this attribute. This
then acts like a touch
type of operation.createdat
attribute's value.modifiedat
value. However, adding or
removing an entity from a nested xRegistry collection MUST update the
modifiedat
value of the parent entity.null
or the same as the existing value, then
the current date/time MUST be used as its new value.createdat
or modifiedat
attributes set to the current date/time due
to the processing rules above MUST use the same value in all cases.Examples:
2030-12-19T06:00:00Z
This specification defines the following API patterns:
/ # Access the Registry
/capabilities # Access available features
/model # Access the model definitions
/export # Retrieve Registry as a doc
/GROUPS # Access a Group Type
/GROUPS/gID # Access a Group
/GROUPS/gID/RESOURCES # Access a Resource Type
/GROUPS/gID/RESOURCES/rID # Default Version of a Resource
/GROUPS/gID/RESOURCES/rID/versions # Versions of a Resource
/GROUPS/gID/RESOURCES/rID/versions/vID # Access a Version of a Resource
While these APIs are shown to be at the root path of a host, implementations MAY choose to prefix them as necessary. However, the same prefix MUST be used consistently for all APIs in the same Registry instance.
Support for any particular API defined by this specification is OPTIONAL,
however, it is STRONGLY RECOMMENDED that server-side implementations support at
least the "read" (e.g. HTTP GET
) operations. Implementations MAY choose to
incorporate authentication and/or authorization mechanisms for the APIs.
If an OPTIONAL HTTP path is not supported by an implementation, then any use of that API MUST generate an error (api_not_found).
If an HTTP method is not supported for a supported HTTP path, then an error (method_not_allowed) MUST be generated.
Implementations MAY support extension APIs, however, the following rules MUST apply:
For example, a new API with an HTTP path of /my-api
is allowed, but APIs with
/model/my-api
or /name
HTTP paths are not.
This specification attempts to follow a standard REST/HTTP processing model. The following key aspects are called out to help understand the overall pattern of the APIs:
PUT
or POST
operation is a full replacement of the entities being
processed. Any missing attributes MUST be interpreted as a request for them
to be deleted. However, attributes that are managed by the server might have
specialized processing in those cases, in particular, mandatory attributes
as well as ones that have default values defined, MUST be reset to their
default values rather than deleted.PATCH
operation MUST only modify the attributes explicitly mentioned
in the request. Any attribute with a value of null
MUST be interpreted
as a request to delete the attribute, and as with PUT
/POST
, server
managed attributes might have specialized processing.PUT
MUST NOT be targeted at xRegistry collections. A POST
or PATCH
MUST be used instead to add entities to the collection, and a
DELETE
MUST be used to delete unwanted entities.POST
operations MUST only be targeted at xRegistry collections, not
individual entities - with the exception of a Resource entity. In that case
a POST
to a Resource URL MUST be treated as an alias for a POST
to the
Resource's versions
collection.In general, if a server is unable to retrieve all of the data intended to be
sent in a response, then an error
(data_retrieval_error) MUST be generated and the
request rejected without any changes being made. However, it is permissible
for a server to attempt some creative processing. For example, if while
processing a GET
the server can only retrieve half of the entities to be
returned at the current point in time, then it could return those with an
indication of there being more (via the pagination
specification). Then during the next GET
request it
could return the remainder of the data - or an error if it is still not
available. Note that if an entity is to be sent, then it MUST be serialized in
its entirety (all attributes, and requested child entities) or an error MUST
be generated.
There might be situations where someone will do a GET
to retrieve data
from a Registry, and then do an update operation to a Registry with that data.
Depending on the use case, they might not want some of the retrieved data
to be applied during the update - for example, they might not want the
epoch
validation checking to occur. Rather than forcing the user to edit
the data to remove the potentially problematic attributes, the following
query parameters MAY be included on write operations to control certain
aspects of the processing:
?noepoch
- presence of this query parameter indicates that any epoch
attribute included in the request MUST be ignored.?nodefaultversionid
- presence of this query parameter indicates that any
defaultversionid
attribute included in the request MUST be ignored.?nodefaultversionsticky
- presence of this query parameter indicates that
any defaultversionsticky
attribute included in the request MUST be ignored.?noreadonly
- presence of this query parameter indicates that any attempt
to update a read-only Resource MUST be silently ignored.Any JSON xRegistry metadata message that represents a single entity (i.e. not a map) MAY include a top-level "$schema" attribute that points to a JSON Schema document that describes the message contents. These notations can be used or ignored by receivers of these messages. There is no requirement for implementation of this specification to persist these values, to include them in responses or to use this information.
One of the goals of xRegistry is to be as broadly supported as possible. Requiring all xRegistry endpoints to support the full range of APIs defined in this specification might not be feasible in all cases. In particular, there might be cases where someone wishes to host a read-only xRegistry server to only expose their documents (and metadata) and therefore the write operations or advanced features (such as inlining or filtering) might not be needed. In those cases, simple file serving HTTP servers, such as blob stores, ought to be sufficient, and in those cases requiring support for query parameters and other advanced features (that could require code) might not always be possible.
To support these simple (no-code) scenarios, this specification is written
such that all of the APIs are OPTIONAL, and all of the query parameters on
the read operations are OPTIONAL (typically specified by saying that they
SHOULD
be supported). However, it is STRONGLY RECOMMENDED that full API
servers support the query parameters when possible to enable a better user
experience, and increase interoperability.
Note that simple file servers SHOULD support exposing Resources where the HTTP
body response contains the Resource's associated "document" as well as the
case where the HTTP response body contains a JSON serialization of the
Resource via the $details
suffix on the URL path. This can be achieved by
creating a secondary sibling file on disk with $details
at the end of its
filename.
The remainder of this specification mainly focuses on the successful interaction
patterns of the APIs. For example, most examples will show an HTTP "200 OK"
as the response. Each implementation MAY choose to return a more appropriate
response based on the specific situation. For example, in the case of an
authentication error the server could return 401 Unauthorized
.
The following sections define the APIs in more detail.
Registry collections (GROUPS
, RESOURCES
and versions
) that are defined
by the Registry Model MUST be serialized according to the
rules defined below.
The serialization of a collection is done as 3 attributes and they MUST adhere to their respective forms as follows:
"COLLECTIONSurl": "URL",
"COLLECTIONScount": UINTEGER,
"COLLECTIONS": {
# Map of entities in the collection, key is the "SINGULARid" of the entity
}
Where:
COLLECTIONS
MUST be the plural name of the collection
(e.g. endpoints
, versions
).COLLECTIONSurl
attribute MUST be a URL that can be used to retrieve
the COLLECTIONS
map via an HTTP(s) GET
(including any necessary
filtering) and MUST be a read-only attribute that MUST
be silently ignored by a server during a write operation. An empty
collection MUST return an HTTP 200 with an empty map ({}
). This attribute
MUST be an absolute URL except when ?doc
is enabled and the collection
is inlined, in which case it MUST be a relative URL.COLLECTIONScount
attribute MUST contain the number of entities in the
COLLECTIONS
map (after any necessary filtering) and MUST
be a read-only attribute that MUST be silently ignored by a server during
a write operation.COLLECTIONS
attribute is a map and MUST contain the entities of the
collection (after any necessary filtering), and MUST use
the SINGULARid
of each entity as its map key.When the COLLECTIONS
attribute is expected to be present in the
serialization, but the number of entities in the collection is zero, it MUST
still be included as an empty map (e.g. {}
).
The set of entities that are part of the COLLECTIONS
attribute is a
point-in-time view of the Registry. There is no guarantee that a future GET
to the COLLECTIONSurl
will return the exact same collection since the
contents of the Registry might have changed. This specification makes no
statement as to whether a subsequent GET
that is missing previously returned
entities is an indication of those entities being deleted or not.
Since collections could be too large to retrieve in one request, when retrieving a collection, the client MAY request a subset by using the pagination specification. Likewise, the server MAY choose to return a subset of the collection using the same mechanism defined in that specification even if the request didn't ask for pagination. The pagination specification MUST only be used when the request is directed at a collection, not at its owning entity (such as the root of the Registry, or at an individual Group or Resource).
In the remainder of the specification, the presence of the Link
HTTP header
indicates the use of the pagination specification
MAY be used for that API.
The requirements on the presence of the 3 COLLECTIONS
attributes varies
between document and API views, and is defined below:
In document view:
COLLECTIONSurl
and COLLECTIONScount
are OPTIONAL.COLLECTIONS
is conditional in responses based on the values in the
Inline Flag. If a collection is part of the flag's value then
COLLECTIONS
MUST be present in the response even if it is empty
(e.g. {}
). If the collection is not part of the flag value then
COLLECTIONS
MUST NOT be included in the response.In API view:
COLLECTIONSurl
is REQUIRED for responses even if there are no entities
in the collection.COLLECTIONScount
is STRONGLY RECOMMENDED for responses even if
there are no entities in the collection. This requirement is not mandated
to allow for cases where calculating the exact count is too costly.COLLECTIONSurl
and COLLECTIONScount
are OPTIONAL for requests and MUST
be silently ignored by the server if present.COLLECTIONS
is conditional in responses based on the values in the
Inline Flag. If a collection is part of the flag's value then
COLLECTIONS
MUST be present in the response even if it is empty
(e.g. {}
). If the collection is not part of the flag value then
COLLECTIONS
MUST NOT be included in the response.COLLECTIONS
is OPTIONAL for requests. See Updating Nested Registry
Collections for more details.When updating an entity that can contain Registry collections, the request
MAY contain the 3 collection attributes. The COLLECTIONSurl
and
COLLECTIONScount
attributes MUST be silently ignored by the server.
If the COLLECTIONS
attribute is present, the server MUST process each entity
in the collection map as a request to create or update that entity according to
the semantics of the HTTP method used. An entry in the map that isn't a valid
entity (e.g. is null
) MUST generate an error (bad_request).
For example:
PUT https://example.com/endpoints/ep1
{
"endpointid": "ep1",
"name": "A cool endpoint",
"messages": {
"mymsg1": { ... },
"mymsg2:" { ... }
}
}
will not only create/update an endpoint
Group with an endpointid
of ep1
but will also create/update its message
Resources (mymsg1
and mymsg2
).
Any error while processing a nested collection entity MUST result in the entire request being rejected.
An absent COLLECTIONS
attribute MUST be interpreted as a request to not
modify the collection at all.
If a client wishes to replace an entire collection, rather than just add new
entities, the client MUST use one of the DELETE
operations on the collection
first.
In cases where an update operation includes attributes meant to be applied
to the "default" Version of a Resource, and the incoming inlined versions
collections includes that "default" Version, the Resource's default Version
attributes MUST be silently ignored. This is to avoid any possible conflicting
data between the two sets of data for that Version. In other words, the
Version attributes in the incoming versions
collection wins.
To better understand this scenario, consider the following HTTP request to
update a Message where the defaultversionid
is v1
:
PUT http://example.com/endpoints/ep1/messages/msg1
{
"messageid": "msg1",
"versionid": "v1",
"name": "Blob Created"
"versions": {
"v1": {
"messageid": "msg1",
"versionid": "v1",
"name": "Blob Created Message Definition"
}
}
}
If the versions
collection were not present with the v1
entity then the
top-level attributes would be used to update the default Version (v1
in this
case). However, because it is present, the request to update v1
becomes
ambiguous because it is not clear if the server is meant to use the top-level
attributes or if it is to use the attributes under the v1
entity of the
versions
collection. When both sets of attributes are the same, then it does
not matter. However, in these cases the name
attributes have different
values. The paragraph above mandates that in these potentially ambiguous cases
the entity in the versions
collection is to be used and the top-level
attributes are to be ignored - for the purposes of updating the "default"
Version's attributes. So, in this case the name
of the default (v1
)
Version will be Blob Created Message Definition
.
Rather than repeating the processing rules for each type of xRegistry entity or Registry collection, the overall pattern is defined once in this section and any entity-, or collection-specific rules will be detailed in the appropriate section in the specification.
This defines the general rules for how to update entities.
Creating or updating entities MAY be done using HTTP PUT
, PATCH
or POST
methods:
PUT PATH-TO-ENTITY[?OPTIONS]
# Process a single entityPATCH PATH-TO-ENTITY[?OPTIONS]
# Process a single entityPATCH PATH-TO-COLLECTION[?OPTIONS]
# Process a set of entitiesPOST PATH-TO-COLLECTION[?OPTIONS]
# Process a set of entitiesBased on the entity being processed, the OPTIONS
available will vary.
The PUT
variant MUST adhere to the following:
PATH-TO-ENTITY
.null
, MUST be interpreted as a request to delete the attribute.The POST
variant MUST adhere to the following:
SINGULARid
of each entity in the map. Note, that in the case of a map of
Versions, the versionid
is used instead.POST
does not support deleting
entities from a collection, so a separate delete operation might be needed
if there are entities that need to be removed.PUT
above.The PATCH
variant when directed at a single entity, MUST adhere to the PUT
semantics defined above with the following exceptions:
null
MUST be interpreted as
a request to delete the attribute.hasdocument
model
aspect set to true
, the URL accessing the entity MUST include the
$details
suffix, and MUST generate an error
(details_required) in the absence of the
$details
suffix. This is because when it is absent, the processing of
the HTTP xRegistry-
headers are already defined with "patch" semantics
so a normal PUT
or POST
can be used instead. Using PATCH
in this
case would mean that the request is also trying to "patch" the Resource's
"document", which this specification does not support at this time.PATCH
MAY be used to create new entities, but as with any of the create
operations, any missing REQUIRED attributes MUST generate an error
(required_attribute_missing).The PATCH
variant when directed at an xRegistry collection, MUST adhere to
the following:
SINGULARid
of each entity in the map. Note, that in the case of a map of
Versions, the versionid
is used instead.PATCH
semantics when directed as a single
entity above.PATCH
of a single entity above.The processing of each individual entity follows the same set of rules:
SINGULARid
already exists then it MUST be
interpreted as a request to update the existing entity. Otherwise, it MUST
be interpreted as a request to create a new entity with that value.SINGULARid
MUST be compared
with the entity's current value and if it differs then an error
(mismatched_id) MUST be generated. This includes both
RESOURCEid
and versionid
in the case of Resources and Versions. This is
to prevent accidentally updating the wrong entity.A successful response MUST return the same response as a GET
to the entity
(or entities) processed, showing their current representation, with the
following exceptions:
POST
case, or a PATCH
directed to an xRegistry collection, the
result MUST contain only the entities processed,
not the entire Registry collection, nor any entities deleted as a result
of processing the request.PUT
or PATCH
cases that are directed to a single entity, for a
newly created entity, the HTTP status MUST be 201 Created
, and it MUST
include an HTTP Location
header with a URL to the newly created entity.
Note that this URL MUST be the same as the self
attribute of that entity.Otherwise an HTTP 200 OK
without an HTTP Location
header MUST be returned.
Note that the response MUST be generated applying the semantics of any
query parameters specified on the request URL (e.g. ?inline
). If an error
occurs while generating the response (e.g. invalid ?filter
), then
an error MUST be generated and the entire operation MUST be undone.
To retrieve a Registry collection, an HTTP GET
MAY be used. The request
MUST be of the form:
GET PATH-TO-COLLECTION
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <URL>;rel=next;count=UINTEGER ?
{
"KEY": { # SINGULARid value
"SINGULARid": "STRING",
... remaining entity attributes ...
} *
}
To retrieve an entity, an HTTP GET
MAY be used. The request MUST be of the
form:
GET PATH-TO-COLLECTION/ID-OF-ENTITY
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"SINGULARid": "STRING",
... remaining entity attributes ...
}
There are two ways to delete entities from a Registry collection:
DELETE
MAY be used. The request MUST
be of the form:DELETE PATH-TO-COLLECTION/ID-OF-ENTITY[?epoch=UINTEGER]
Where:
DELETE
directed to the meta
sub-object is not supported and MUST generate an error
(method_not_allowed).The following query parameter SHOULD be supported by servers:
epoch
epoch
value matches the entity's current epoch
value
and if it differs then an error (mismatched_epoch) MUST
be generated.For non-Resource entities:
DELETE PATH-TO-COLLECTION
{
"KEY": { # SINGULARid of entity
"epoch": UINTEGER ?
} *
} ?
or
For Resource entities (see below for more details):
DELETE PATH-TO-COLLECTION
{
"KEY": { # SINGULARid of entity
"meta": {
"epoch": UINTEGER ?
} ?
} *
} ?
Where:
SINGULARid
of the entity.epoch
value is specified for an entity then the server MUST check
to ensure that the value matches the entity's current epoch
value and if it
differs then an error (mismatched_epoch) MUST be
generated.epoch
attribute is located under the
meta
sub-object (and not as a top-level entity attribute), if included
in the DELETE
request, it MUST appear under a meta
sub-object. Any
additional epoch
at the top-level MUST be silently ignored. Additionally,
DELETE
request of Resources that only has epoch
at a top-level attribute,
but not as a meta
attribute, MUST generate an error
(misplaced_epoch) as it is likely that the client is
using the Resource's default Version epoch
value by mistake. A top-level
epoch
in the presence of a meta
epoch
MUST be ignored.KEY
value.Whether the request is to delete a single entity or multiple, deleting an entity MUST delete all children entities as well - meaning, any entities within any nested Registry collections.
Any error MUST result in the entire request being rejected.
A successful response MUST return either:
HTTP/1.1 204 No Content
with an empty HTTP body, or:
HTTP/1.1 200 OK
if, as an extension, the server chooses to return additional data in the HTTP body.
The Registry entity represents the root of a Registry and is the main entry-point for traversal and discovery.
The serialization of the Registry entity adheres to this form:
{
"specversion": "STRING",
"registryid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"capabilities": { Registry capabilities }, ? # Only if inlined
"model": { Registry model }, ? # Only if inlined
# Repeat for each Group type
"GROUPSurl": "URL", # e.g. "endpointsurl"
"GROUPScount": UINTEGER, # e.g. "endpointscount"
"GROUPS": { GROUPS collection } ? # Only if inlined
}
The Registry entity includes the following common attributes:
registryid
- REQUIRED in API and document
views. OPTIONAL in requests.self
- REQUIRED in API and document views. OPTIONAL
in requests.shortself
- OPTIONAL in API and document views,
based on the shortself
capability. OPTIONAL in requests.xid
- REQUIRED in API and document views. OPTIONAL in
requests.epoch
- REQUIRED in API and document view. OPTIONAL
in requests.name
- OPTIONAL.description
- OPTIONAL.documentation
- OPTIONAL.labels
- OPTIONAL.createdat
- REQUIRED in API and document views.
OPTIONAL in requests.modifiedat
- REQUIRED in API and document views.
OPTIONAL in requests.and the following Registry level attributes:
specversion
AttributeType: String
Description: The version of this specification that the serialization adheres to
Constraints:
Examples:
1.0
model
AttributeType: Registry Model
Description: A description of the features, extension attributes, Groups and Resources supported by this Registry. See Registry Model
Constraints:
GROUPS
CollectionsType: Set of Registry Collections
Description: A list of Registry collections that contain the set of Groups supported by the Registry.
Constraints:
To retrieve the Registry, its metadata attributes, and Groups, an HTTP GET
MAY be used.
The request MUST be of the form:
GET /[?specversion=...]
The following query parameter SHOULD be supported by servers:
specversion
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"specversion": "STRING",
"registryid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"capabilities": { Registry capabilities }, ? # Only if inlined
"model": { Registry model }, ? # Only if inlined
# Repeat for each Group type
"GROUPSurl": "URL", # e.g. "endpointsurl"
"GROUPScount": UINTEGER, # e.g. "endpointscount"
"GROUPS": { GROUPS collection } ? # Only if inlined
}
Examples:
Retrieve a Registry that has 2 types of Groups (endpoints
and
schemagroups
):
GET /
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"specversion": "1.0-rc1",
"registryid": "myRegistry",
"self": "https://example.com/",
"xid": "/",
"epoch": 1,
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"endpointsurl": "https://example.com/endpoints",
"endpointscount": 42,
"schemagroupsurl": "https://example.com/schemagroups",
"schemagroupscount": 1
}
Another example where:
schemagroups
Group to be inlined in the response.endpoints
Group has one extension attribute defined.GET /?inline=schemagroups,model
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"specversion": "1.0-rc1",
"registryid": "myRegistry",
"self": "https://example.com/",
"xid": "/",
"epoch": 1,
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"model": {
... xRegistry spec-defined attributes excluded for brevity ...
"groups": {
"endpoints": {
"plural": "endpoints",
"singular": "endpoint",
"attributes": {
... xRegistry spec-defined attributes excluded for brevity ...
"shared": {
"name": "shared",
"type": "boolean"
}
},
"resources": {
"messages": {
"plural": "messages",
"singular": "message",
"attributes": {
... xRegistry spec-defined attributes excluded for brevity ...
"*": {
type: "any"
}
},
"maxversions": 1
}
}
},
"schemagroups": {
"plural": "schemagroups",
"singular": "schemagroup",
... xRegistry spec-defined attributes excluded for brevity ...
"resources": {
"schemas": {
"plural": "schemas",
"singular": "schema",
... xRegistry spec-defined attributes excluded for brevity ...
"maxversions": 1
}
}
}
}
},
"endpointsurl": "https://example.com/endpoints",
"endpointscount": 42,
"schemagroupsurl": "https://example.com/schemagroups",
"schemagroupscount": 1,
"schemagroups": {
"mySchemas": {
"schemaid": "mySchemas",
# Remainder of schemagroup is excluded for brevity
}
}
}
To update the Registry entity, an HTTP PUT
or PATCH
MAY be used.
The request MUST be of the form:
PUT /
or
PATCH /
Content-Type: application/json; charset=utf-8
If-Match: "UINTEGER|*" ?
{
"registryid": "STRING", ?
"epoch": UINTEGER, ?
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP", ?
"modifiedat": "TIMESTAMP", ?
"model": { Registry model }, ?
# Repeat for each Group type
"GROUPS": { GROUPS collection } ?
}
Where:
'model
attribute if the Registry model
definitions are to be updated as part of the request. See Updating the
Registry Model for more information.
If present, the Registry's model MUST be updated prior to any entities being
updated. A value of null
MUST generate an error
(invalid_data).A successful response MUST include the same content that an HTTP GET
on the Registry would return, and be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"specversion": "STRING",
"registryid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
# Repeat for each Group type
"GROUPSurl": "URL",
"GROUPScount": UINTEGER
}
Note that the response MUST NOT include the model
attribute, nor any
inlined GROUPS collections.
Examples:
Updating a Registry's metadata
PUT /
Content-Type: application/json; charset=utf-8
{
"registryid": "myRegistry",
"name": "My Registry",
"description": "An even cooler registry!"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"specversion": "1.0-rc1",
"registryid": "myRegistry",
"self": "https://example.com/",
"xid": "/",
"epoch": 2,
"name": "My Registry",
"description": "An even cooler registry!",
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"endpointsurl": "https://example.com/endpoints",
"endpointscount": 42,
"schemagroupsurl": "https://example.com/schemagroups",
"schemagroupscount": 1
}
In order to programmatically discover which capabilities are supported by an implementation, servers MUST support exposing this information via a "capabilities" map that lists each supported feature along with any related configuration detail that will help in successful usage of that feature.
The "key" of the capabilities-map is the "name" of each feature, and the
"value" is a feature specific set of configuration values. With the most basic
being a BOOLEAN
value of true
to indicate support for the feature.
The capabilities-map MAY be retrieved via two mechanisms:
GET
request to the /capabilities
API and MUST be supported
by all compliant implementations. However, as with all defined APIs,
security/access controls MAY be mandated.capabilities
attribute MAY be requested via the ?inline
query parameter. Note that support for the ?inline
query parameter itself
is OPTIONAL. The capabilities
attribute MUST only appear when explicitly
requested by the client via the ?inline
query parameter.Regardless of the retrieval mechanism, the format of the capabilities-map MUST be of the form:
{
"flags": [ "STRING" * ], ?
"mutable": [ "STRING" * ], ?
"pagination": BOOLEAN, ?
"schemas": [ "STRING" * ], ?
"shortself": BOOLEAN, ?
"specversions": [ "STRING" ], ?
"sticky": BOOLEAN, ?
"STRING": ... capability configuration ... * // Extension capabilities
}
Where:
"STRING"
MUST be the name of the capability. This specification places
no restriction on the "STRING"
value, other than it MUST be unique across
all capabilities and not be the empty string. It is RECOMMENDED that
extensions use some domain-specific name to avoid possible conflicts with
other extensions.All capability values, including extensions, MUST be defined as one of the following:
Absence of a capability in the capability map is an indication of that feature not being supported. All supported extensions MUST be included in the list.
Absence, presence, or configuration values of a feature in the map MAY vary based on the authorization level of the client making the request.
The following defines the specification-defined capabilities:
flags
flags
collections
, doc
, epoch
, filter
, inline
, nodefaultversionid
,
nodefaultversionsticky
, noepoch
, noreadonly
, offered
, schema
,
setdefaultversionid
, specversion
."flags": [ "filter", "inline" ]
# Just these 2mutable
mutable
entities
refers to Groups, Resources, Versions and the Registry
itself. model
refers to the ability to modify the Registry model.
capabilities
refers to the ability to modify (and configure) the
server. Presence in this list does not guarantee that a client can edit
all items of that type. For example, some Resources might still be read-only
even if the client has the ability to edit Resources in general.capabilities
, entities
, model
.pagination
pagination
true
).false
.schemas
schemas
jsonSchema/2020-12
), and SHOULD be of the form NAME[/VERSION]
.
All implementations of this specification MUST support
xRegistry-json/1.0-rc1
(the JSON serialization as defined by this
specification).xRegistry-json/1.0-rc1
MUST be included in the list.xRegistry-json/1.0-rc1
.shortself
shortself
shortself
attribute MUST be included
in the server serialization of the entities within the Registry (value of
true
).false
.specversions
specversions
1.0-rc1
.1.0-rc1
MUST be included in the list.1.0-rc1
.sticky
sticky
setdefaultversionsticky
attribute to true
is allowed.true
.The list of values for the arrays MUST be case-insensitive and MAY include extension values.
For clarity, servers MUST include all known capabilities in the serialization, even if they are set to their default values or have empty lists.
If supported, updates to the server's capabilities MAY be done via an HTTP
PUT
, or PATCH
, to the /capabilities
API, or by updating the capabilities
attribute on the root of the Registry. As with other APIs, a PUT
MUST be
interpreted as a request to update the entire set of capabilities and any
missing capability MUST be interpreted as a request to reset it to its default
value. If a PATCH
is used then each capability included MUST be fully
specified, and fully replaced by the incoming value. In other words, PATCH
is done at a capability level not any deeper within the JSON structure.
The request to the /capabilities
API MUST be of the form:
PUT /capabilities
Content-Type: application/json; charset=utf-8
{ ... Capabilities map ... }
or
PATCH /capabilities
Content-Type: application/json; charset=utf-8
{ ... Capabilities map ... }
Where:
PUT
, and the full representation of just the modified
capabilities in the case of PATCH
.A successful response MUST include a full representation of all of the capabilities of the Registry and be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{ ... Capabilities map ... }
Updates via the capabilities
attribute follows the same attribute
update semantics as the other Registry level attributes. Note that using
an HTTP PATCH
to update the Registry's attributes MAY include the
capabilities
attribute, however, it MUST be processed with the PATCH
semantics as well.
During the processing of a request to update the capabilities, the semantic change MUST NOT take effect until after the processing of the current request. Note that if the response includes the serialization of the Registry's capabilities, then the changes MUST appear in that serialization.
For any capability that is an array of strings, a value of "*"
MAY be used to
indicate that the server MUST replace "*"
with the full set of items that
are available. An error (capability_error) MUST be
generated if "*"
appears with any other value in the list. "*"
MUST NOT
appear in the serialization in any server's response.
Regardless of the mechanism used to update the capabilities, the Registry's
epoch
value MUST be incremented.
In order for a client to discover the list of available values for each
capability, an HTTP GET
MAY be sent to the /capabilities
API with the
?offered
query parameter and the response MUST adhere to the following
(which borrows many of the same structure from the /model
API):
GET /capabilities?offered
{
"STRING": {
"type": "TYPE",
"item": {
"type": "TYPE"
}, ?
"enum": [ VALUE, * ], ?
"min": VALUE, ?
"max": VALUE, ?
"documentation": "URL" ?
}, *
}
Where:
STRING
MUST be the capability name.TYPE
MUST be one of boolean
, string
, integer
, decimal
, uinteger
,
array
as defined in Attributes and
Extensions."type"
is array
, "item.type"
MUST be one of boolean
, string
,
integer
, decimal
, uinteger
, otherwise "item"
MUST be absent."enum"
, when specified, contains a list of zero or more VALUE
s whose
type MUST match either "type"
or "item.type"
if "item"
is "array"
.
This indicates the list of allowable values for this capability."min"
and "max"
, when specified, MUST match the same type as either
"type"
or "item.type"
if "item"
is "array"
. These indicate the
minimum or maximum (inclusive) value range of this capability. When not
specified, there is no stated lower (or upper) limit."documentation"
provides a URL with additional information about the
capability.For example:
GET /capabilities?offered
{
"flags": {
"type": "string",
"enum": [ "collections", "doc", "epoch", "filter", "inline",
"nodefaultversionid", "nodefaultversionsticky", "noepoch", "noreadonly",
"offered", "schema", "setdefaultversionid", "specversion" ]
},
"pagination": {
"type": "boolean",
"enum": [ false, true ]
},
"schemas": {
"type": "string",
"enum": [ "xRegistry-json/1.0-rc1" ]
},
"shortself": {
"type": "boolean",
"enum": [ false, true ]
},
"specversions": {
"type": "string",
"enum": [ "xRegistry-json/1.0-rc1" ]
},
"sticky": {
"type": "boolean",
"enum": [ true ]
}
}
The enum of values allows for some special cases:
*
as a wildcard character in a value
to indicate zero or more unspecified characters MAY appear at that location
in the value string.A request to update a capability with a value that is compliant with the
output of the /capabilities?offered
MAY still generate an error
(capability_error) if the server determines it cannot
support the request. For example, due to authorization concerns or the value,
while syntactically valid, isn't allowed in certain situations.
For clarity, even in cases where there is no variability allowed with certain
capabilities they SHOULD still be listed in both the /capabilities
API
and the /capabilities?offered
API to maximize discoverability. For example,
if pagination
is not supported, then a server SHOULD still include:
"pagination": false
in the /capabilities
output, and
"pagination": {
"type": "boolean",
"enum": [ false ]
}
in the /capabilities?offered
output (assuming both APIs are supported).
The Registry model defines the Groups, Resources, extension attributes and changes to specification-defined attributes. 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.
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 int
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 model. In other words, the Registry's model consists of the specification-defined attributes overlaid with the attributes that are explicitly-defined as part of a model create/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.
Additionally, is it 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 schema for an empty Registry can be found here, while a schema for a sample xRegistry (with Groups and Resources) can be found here.
The Registry model can be retrieved two ways:
GET
to the /model
API. This is
useful when management of the Registry's model is needed independent of the
entities within the Registry. See Retrieving the Registry
Model for more information.model
attribute be
inlined. This is useful when it is desirable to view the entire Registry as
a single document - such as an "export" type of scenario. See the
Retrieving the Registry section for more
information on this option.Regardless of how the model is retrieved, the overall format is as follows:
{
"labels": { "STRING": "STRING" * }, ?
"attributes": { # Registry level extensions
"STRING": { # Attribute name
"name": "STRING", # Same as attribute's key
"type": "TYPE", # boolean, string, array, object, ...
"target": "STRING", ? # If "type" is "xid"
"namecharset": "STRING", ? # If "type" is "object"
"description": "STRING",
"enum": [ VALUE * ], ? # Array of values of type "TYPE"
"strict": BOOLEAN, ? # Just "enum" values or not. Default=true
"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
"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
"VALUE": {
"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", ?
"modelversion": "STRING", ? # Version of the group model
"compatiblewith": "URI", ? # Statement of compatibility with model spec
"labels": { "STRING": "STRING" * }, ?
"attributes": { ... }, ? # See "attributes" above
"resources": {
"STRING": { # Key=plural name, e.g. "messages"
"plural": "STRING", # e.g. "messages"
"singular": "STRING", # e.g. "message"
"description": "STRING", ?
"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
"singleversionroot": BOOLEAN, ? # enforce single root. Default=false
"typemap": MAP, ? # contenttype mappings
"modelversion": "STRING", ? # Version of the resource model
"compatiblewith": "URI"`, ? # Statement of compatibility with model spec
"labels": { "STRING": "STRING" * }, ?
"attributes": { ... }, ? # Version attributes/extensions
"metaattributes": { ... } ? # Resource attributes/extensions
} *
} ?
} *
} ?
}
The following describes the attributes of Registry model:
modelversion
1.2
compatiblewith
https://raw.githubusercontent.com/xregistry/spec/refs/heads/main/schema/model.json
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
attribute. 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.
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. The value of this model attribute MUST be
a "xid template" of one of the following forms:
/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]
- a Version or Resource type name. 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./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. Absolute URIs/URLs are not constrained by the presence of
a target
value.ximport
to reference another model entity definition. In
other words, target
is not transitive./endpoints/messages
attributes."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_character).
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
.
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).
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 is not part of the enum
set MUST generate an error
(invalid_data).
This attribute has no impact when enum
is absent or an empty array.true
.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 SINGULARid
, 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_data) 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 (default
) is defined.attributes."STRING".default
null
value of the type specified by the
attributes."STRING".type
model attribute and MUST only be used for
scalar types.null
.required
aspect set to true
.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.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 each value of the attribute is the key 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
"VALUE"
(case-sensitive) then the siblingattributes
MUST
be included in the model as siblings to this attribute.
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
"VALUE"
MUST NOT be an empty string.
ifvalues
"VALUE"
MUST NOT start with the ^
(caret) character as its
presence at the beginning of "VALUE"
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".labels
labels
]((#model.labels) above.groups."STRING".attributes
attributes
above.groups."STRING".resources
groups.resources.plural
)
of the Resource type (RESOURCES
).groups."STRING"
.resources."STRING"`
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".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
.ancestor
is the same as its
versionid
).createdat
date.versionid
attribute.
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.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.true
if maxversions
is one (1
).groups."STRING".resources."STRING".hasdocument
Type: Boolean (true
or false
, case-sensitive).
OPTIONAL.
Indicates whether or not Resources of this type can have a document
associated with it. If false
then the xRegistry metadata becomes "the
document". Meaning, an HTTP GET
to the Resource's URL will return the
xRegistry metadata in the HTTP body. The xRegistry-
HTTP headers MUST
NOT be used for requests or response messages for these Resources.
Use of $details
on the request URLs MAY be used to provide consistency
with the cases where this attribute is set to true
- but the output
remains the same.
A value of true
does not mean that these Resources are guaranteed to
have a non-empty document, and an HTTP GET
to the Resource MAY return an
empty HTTP body.
When not specified, the default value MUST be true
.
A value of true
indicates that Resource of this type supports a separate
document to be associated with it.
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. This is useful to avoid creating multiple roots. When this
attribute is set to true
, 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.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
instance 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):
binary
json
string
Implementations 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 RESOURCEbase64
attribute,
even if the contenttype
is of the same type of the xRegistry metadata
(e.g. application/json
). This is useful when it is desireable 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 formating 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 json
text/plain
: mapped to string
Example:
"typemap": {
"text/*": "string",
"text/mine": "json"
}
groups."STRING".resources."STRING".labels
attributes
above.groups."STRING".resources."STRING".attributes
attributes
above and
metaattributes
below.groups.resources.attributes
names MUST NOT overlap with the
list of groups.resource.metaattributes
names.groups."STRING".resources."STRING".metaattributes
attributes
above.meta
sub-object of the Resource.groups.resources.attributes
names MUST NOT overlap with the
list of groups.resource.metaattributes
names.To retrieve the Registry Model as a stand-alone entity, an HTTP GET
MAY be
used.
Registries MAY support exposing the model in a variety of well-defined schema
formats. The schemas
capabilities attribute MUST expose the set of schema
formats available.
The resulting schema document MUST include the full Registry model - meaning all specification-defined attributes, extension attributes, Group types, and Resource types.
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, the full model definition of the Registry level attributes can be found in model.json, and the Group and Resource level attributes can be found in this sample sample-model.json.
The request MUST be of the form:
GET /model[?schema=NAME[/VERSION]]
Where:
?schema
query parameter MUST be one of the valid
schema
capabilities values (case-insensitive).xRegistry-json/1.0-rc1
.Implementations of this specification MUST support xRegistry-json/1.0-rc1
.
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: ...
... xRegistry model in a schema specific format ...
Where:
?schema
query parameter.VERSION
is not specified as part of the ?schema
query parameter
then the server MAY choose any schema version of the specified schema format.
However, it is RECOMMENDED that the newest supported version be used.If the specified schema format is not supported then an error (invalid_data) MUST be generated.
When the schema
is xRegistry-json/1.0-rc1
then the response MUST be of the
form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"labels": { "STRING": "STRING" * }, ?
"attributes": {
"STRING": {
"name": "STRING",
"type": "TYPE",
"target": "STRING", ?
"namecharset": "STRING", ?
"description": "STRING", ?
"enum": [ VALUE * ], ?
"strict": BOOLEAN, ?
"readonly": BOOLEAN, ?
"immutable": BOOLEAN, ?
"required": BOOLEAN, ?
"default": VALUE, ?
"attributes": { ... }, ?
"item": { ... }, ?
"ifvalues": {
"VALUE": {
"siblingattributes": { ... }
} *
} ?
} *
},
"groups": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"description": "STRING", ?
"modelversion": "STRING", ?
"compatiblewith": "URI", ?
"labels": { "STRING": "STRING" * }, ?
"attributes": { ... }, ?
"resources": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"description": "STRING", ?
"maxversions": UINTEGER, ?
"setversionid": BOOLEAN, ?
"setdefaultversionsticky": BOOLEAN, ?
"hasdocument": BOOLEAN, ?
"singleversionroot": BOOLEAN, ?
"typemap": MAP, ?
"modelversion": "STRING", ?
"compatiblewith": "URI", ?
"labels": { "STRING": "STRING" * }, ?
"attributes": { ... }, ?
"metaattributes": { ... } ?
} *
} ?
} *
} ?
}
Examples:
Retrieve a Registry model that has one extension attribute on the
endpoints
Group, and supports returning the schema of the Registry
as JSON Schema:
GET /model
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"attributes": {
... xRegistry spec-defined attributes excluded for brevity ...
},
"groups": {
"endpoints": {
"plural": "endpoints",
"singular": "endpoint",
"attributes": {
... xRegistry spec-defined attributes excluded for brevity ...
"shared": {
"name": "shared",
"type": "boolean"
}
},
"resources": {
"messages": {
"plural": "messages",
"singular": "message",
... xRegistry spec-defined attributes excluded for brevity ...
"*": {
type: "any"
}
},
"metaattributes": {
... xRegistry spec-defined attributes excluded for brevity ...
"*": {
type: "any"
}
}
}
}
}
}
}
To update the Registry model, the new full representation of the model
MAY be included on an HTTP PUT
to the Registry Entity in the model
attribute, or a PUT
MAY be done to the /model
API. Note that PATCH
is not supported via the /model
API.
While the remainder of this section is presented within the scope of the
/model
API, the processing rules of the model definition MUST also apply
when it is updated via the model
attribute on the Registry entity.
The request MUST be of the form:
PUT /model
Content-Type: application/json; charset=utf-8
{
"labels": { "STRING": "STRING" * }, ?
"attributes": {
"STRING": {
"name": "STRING",
"type": "TYPE",
"target": "STRING", ?
"namecharset": "STRING", ?
"description": "STRING", ?
"enum": [ VALUE * ], ?
"strict": BOOLEAN, ?
"readonly": BOOLEAN, ?
"immutable": BOOLEAN, ?
"required": BOOLEAN, ?
"default": VALUE, ?
"attributes": { ... }, ? # For nested object
"item": { ... }, ? # For nested map, array
"ifvalues": {
"VALUE": {
"siblingattributes": { ... }
} *
} ?
} *
},
"groups": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"description": "STRING", ?
"modelversion": "STRING", ?
"compatiblewith": "URI", ?
"labels": { "STRING": "STRING" * }, ?
"attributes": { ... }, ? # See "attributes" above
"resources": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"description": "STRING", ?
"maxversions": UINTEGER, ?
"setversionid": BOOLEAN, ?
"setdefaultversionsticky": BOOLEAN, ?
"hasdocument": BOOLEAN, ?
"singleversionroot": BOOLEAN, ?
"typemap": MAP, ?
"modelversion": "STRING", ?
"compatiblewith": "URI", ?
"labels": { "STRING": "STRING" * }, ?
"attributes": { ... }, ? # Version attributes/extensions
"metaattributes": { ... } ? # Resource attributes/extensions
} *
} ?
} *
} ?
}
Where:
See Registry Model for more details about the list of allowable changes to a model.
A successful response MUST include a full representation of the Registry model and be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"labels": { "STRING": "STRING" * }, ?
"attributes": {
"STRING": {
"name": "STRING",
"type": "TYPE",
"target": "STRING", ?
"namecharset": "STRING", ?
"description": "STRING", ?
"enum": [ VALUE * ], ?
"strict": BOOLEAN, ?
"readonly": BOOLEAN, ?
"immutable": BOOLEAN, ?
"required": BOOLEAN, ?
"default": VALUE, ?
"attributes": { ... }, ?
"item": { ... }, ?
"ifvalues": {
"VALUE": {
"siblingattributes": { ... }
} *
} ?
} *
},
"groups": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"description": "STRING", ?
"modelversion": "STRING", ?
"compatiblewith": "URI", ?
"labels": { "STRING": "STRING" * }, ?
"attributes": { ... }, ?
"resources": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"description": "STRING", ?
"maxversions": UINTEGER, ?
"setversionid": BOOLEAN, ?
"setdefaultversionsticky": BOOLEAN, ?
"hasdocument": BOOLEAN, ?
"singleversionroot": BOOLEAN, ?
"typemap": MAP, ?
"modelversion": "STRING", ?
"compatiblewith": "URI", ?
"labels": { "STRING": "STRING" * }, ?
"attributes": { ... }, ?
"metaattributes": { ... } ?
} *
} ?
} *
} ?
}
Examples:
Update a Registry's model to add a new Group type:
PUT /model
Content-Type: application/json; charset=utf-8
{
"groups": {
"endpoints": {
"plural": "endpoints",
"singular": "endpoint",
"attributes": {
"shared": {
"name": "shared",
"type": "boolean"
}
},
"resources": {
"messages": {
"plural": "messages",
"singular": "message",
"attributes": {
"*": {
type: "any"
}
}
}
}
},
"schemagroups": {
"plural": "schemagroups",
"singular": "schemagroup",
"resources": {
"schemas": {
"plural": "schemas",
"singular": "schema"
}
}
}
}
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"attributes": {
... xRegistry spec-defined attributes excluded for brevity ...
},
"groups": {
"endpoints" {
"plural": "endpoints",
"singular": "endpoint",
"attributes": {
... xRegistry spec-defined attributes excluded for brevity ...
"shared": {
"name": "shared",
"type": "boolean"
}
},
"resources": {
"messages": {
"plural": "messages",
"singular": "message",
... xRegistry spec-defined attributes excluded for brevity ...
"attributes": {
"*": {
type: "any"
}
}
"maxversions": 1
}
}
},
"schemagroups": {
"plural": "schemagroups",
"singular": "schemagroup",
"resources": {
"schemas": {
"plural": "schemas",
"singular": "schema"
... xRegistry spec-defined attributes excluded for brevity ...
}
}
}
}
}
When a Resource type definition is to be shared between Groups, rather than
creating a duplicate Resource definition, the ximport
mechanism MAY be used
instead. When defining the Resources of a Group, a special Resource "plural"
name MAY be used to reference other Resource definitions from within the same
Registry. For example, the following abbreviated model definition defines
one Resource type (messages
) under the messagegroups
Group, that is
also used by the endpoints
Group.
"model": {
"groups": {
"messagegroups": {
"plural": "messagegroups",
"singular": "messagegroup",
"resources": {
"messages": {
"plural": "messages",
"singular": "message"
}
}
},
"endpoints": {
"plural": "endpoints",
"singular": "endpoint",
"resources": {
"ximport": [ "messagegroups/messages" ]
}
}
}
}
The format of the ximport
specification is:
"ximport": [ "GROUPS/RESOURCES", * ]
where:
ximport
resides.Since the resources
attribute is a map, use of the ximport
feature MUST
only be used once per Group definition.
Additional locally defined Resources MAY be defined within a Group that uses
the ximport
feature, however, Resource plural
and singular
values
MUST be unique across all imported and locally defined Resources. Locally
defined Resources MUST NOT use ximport
as a plural
or singular
name.
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 processing the request and MUST return the
expanded model in response to the request for the model. The includes MUST NOT
be processed again at a later time. A request to re-evaluate the includes can
be done via a subsequent model update operation. Note, this means that when
the model is subsequently retrieved (e.g. via an HTTP GET
) the include
directives MUST NOT be in the response.
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": {
"plural": "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"
}
The /export
API MUST be an alias for
GET /?doc&inline=*,model,capabilities". If supported, it MUST only support the
GET` HTTP method. This API was created:
?doc
removes.Query parameters MAY be included on the request and any ?inline
flag
specified MUST override the default value defined above.
Groups represent entities that typically act as a collection mechanism for related Resources. However, it is worth noting that Groups do not have to have Resources associated with them. It is possible to have Groups be the main (or only) entity of a Registry. Each Group type MAY have any number of Resource types within it. This specification does not define how the Resources within a Group type are related to each other.
The serialization of a Group entity adheres to this form:
{
"GROUPid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
# Repeat for each Resource type in the Group
"RESOURCESurl": "URL", # e.g. "messagesurl"
"RESOURCEScount": UINTEGER, # e.g. "messagescount"
"RESOURCES": { RESOURCES collection } ? # If inlined
}
Groups include the following common attributes:
GROUPid
- REQUIRED in API and document views.
OPTIONAL in requests.self
- REQUIRED in API and document views. OPTIONAL in
requests.shortself
- OPTIONAL in API and document views,
based on the shortself
capability. OPTIONAL in requests.xid
- REQUIRED in API and document views. OPTIONAL in
requests.epoch
- REQUIRED in API and document views. OPTIONAL in
requests.name
- OPTIONAL.description
- OPTIONAL.documentation
- OPTIONAL.labels
- OPTIONAL.createdat
- REQUIRED in API and document views.
OPTIONAL in requests.modifiedat
- REQUIRED in API and document views.
OPTIONAL in requests.and the following Group level attributes:
RESOURCES
CollectionsType: Set of Registry Collections.
Description: A list of Registry collections that contain the set of Resources supported by the Group.
Constraints:
To retrieve a Group collection, an HTTP GET
MAY be used.
The request MUST be of the form:
GET /GROUPS
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <URL>;rel=next;count=UINTEGER ?
{
"KEY": { # GROUPid
"GROUPid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
# Repeat for each Resource type in the Group
"RESOURCESurl": "URL", # e.g. "messagesurl"
"RESOURCEScount": UINTEGER, # e.g. "messagescount"
"RESOURCES": { RESOURCES collection } ? # If inlined
} *
}
Examples:
Retrieve all entities in the endpoints
Group:
GET /endpoints
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <https://example.com/endpoints&page=2>;rel=next;count=100
{
"ep1": {
"endpointid": "ep1",
"self": "https://example.com/endpoints/ep1",
"xid": "/endpoints/ep1",
"epoch": 1,
"name": "A cool endpoint",
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"messagesurl": "https://example.com/endpoints/ep1/messages",
"messagescount": 5
},
"ep2": {
"endpointid": "ep2",
"self": "https://example.com/endpoints/ep2",
"xid": "/endpoints/ep2",
"epoch": 3,
"name": "Redis Queue",
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"messagesurl": "https://example.com/endpoints/ep2/messages",
"messagescount": 1
}
}
Notice that the Link
HTTP header is present, indicating that there
is a second page of results that can be retrieved via the specified URL,
and that there are total of 100 items in this collection.
Creating or updating Groups via HTTP MAY be done by using the HTTP PUT
,
PATCH
or POST
methods:
PUT /GROUPS/gID
# Create/update a single GroupPATCH /GROUPS/gID
# Create/update a single GroupPATCH /GROUPS
# Create/update multiple Groups of type GROUPSPOST /GROUPS
# Create/update multiple Groups of type GROUPSThe processing of the above APIs is defined in the Creating or Updating Entities section.
POST /
# Create/update multiple Groups of varying typesThis API is very similar to the POST /GROUPS
above except that the HTTP
body MUST be a map of Group types as shown below:
{
"endpoints": {
"endpoint1": { ... Group endpoint1's xRegistry metadata ... },
"endpoint2": { ... Group endpoint2's xRegistry metadata ... }
},
"schemagroups": {
"schemagroup1": { ... Group schemagroup1's xRegistry metadata ... },
"schemagroup2": { ... Group schemagroup2's xRegistry metadata ... }
}
}
Notice the format is almost the same as what a PUT /
would look like if the
request wanted to update the Registry's attributes and define a set of Groups,
but without the Registry's attributes. This allows for an update of the
specified Groups without modifying the Registry's attributes.
The response in this case MUST be a map of the Group types with just the Groups that were processed as part of the request.
Each individual Group definition MUST adhere to the following:
{
"GROUPid": "STRING", ?
"self": "URL", ?
"shortself": "URL", ?
"xid": "XID", ?
"epoch": UINTEGER, ?
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP", ?
"modifiedat": "TIMESTAMP", ?
# Repeat for each Resource type in the Group
"RESOURCESurl": "URL", # e.g. "messagesurl"
"RESOURCEScount": UINTEGER, # e.g. "messagescount"
"RESOURCES": { RESOURCES collection } ?
}
Each individual Group in a successful response MUST adhere to the following:
{
"GROUPid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
# Repeat for each Resource type in the Group
"RESOURCESurl": "URL", # e.g. "messagesurl"
"RESOURCEScount": UINTEGER # e.g. "messagescount"
}
Examples:
Targeted request to create a specific Group by GROUPid
:
PUT /endpoints/ep1
Content-Type: application/json; charset=utf-8
{
"endpointid": "ep1",
... remainder of Endpoint 'ep1' definition ...
}
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Location: https://example.com/endpoints/ep1
{
"endpointid": "ep1",
... remainder of Endpoint 'ep1' definition ...
}
Multiple Groups specified in the HTTP body:
POST /endpoints
Content-Type: application/json; charset=utf-8
{
"ep1": {
"endpointid": "ep1",
... remainder of ep1 definition ...
},
"ep2": {
"endpointid": "ep2",
... remainder of ep2 definition ...
}
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"ep1": {
"endpointid": "ep1",
... remainder of ep1 definition ...
},
"ep2": {
"endpointid": "ep2",
... remainder of ep2 definition ...
}
}
To retrieve a Group, an HTTP GET
MAY be used.
The request MUST be of the form:
GET /GROUPS/gID
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"GROUPid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
# Repeat for each Resource type in the Group
"RESOURCESurl": "URL", # e.g. "messagesurl"
"RESOURCEScount": UINTEGER, # e.g. "messagescount"
"RESOURCES": { RESOURCES collection } ? # If inlined
}
Examples:
Retrieve a single endpoints
Group:
GET /endpoints/ep1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"GROUPid": "ep1",
"self": "https://example.com/endpoints/ep1",
"xid": "/endpoints/ep1",
"epoch": 1,
"name": "myEndpoint",
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"messagesurl": "https://example.com/endpoints/ep1/messages",
"messagescount": 5
}
To delete one or more Groups, an HTTP DELETE
MAY be used:
DELETE /GROUPS/gID[?epoch=UINTEGER]
DELETE /GROUPS
The processing of these two APIs is defined in the Deleting Entities in a Registry Collection section.
Resources typically represent the main entity that the Registry is managing.
Each Resource is associated with a Group to aid in their discovery and to show
a relationship with Resources in that same Group. Resources appear within the
Group's RESOURCES
collection.
Resources, like all entities in the Registry, can be modified but Resources
can also have a version history associated with them, allowing for users to
retrieve previous Versions of the Resource. In this respect, Resources have
a 2-layered definition. The first layer is the Resource entity itself,
and the second layer is its versions
collection - the version history of
the Resource.
The Resource entity serves three purposes:
1 - It represents the collection for the historical Versions of the data being
managed. This is true even if the Resource type is defined to not use
versioning, meaning the number of Versions allowed is just one. The
Versions will appear as a nested entity under the versions
attribute.
2 - It is an alias for the "default" Version of the Resource. And most
operations directed at the URL of the Resource will act upon that Version,
not the Resource itself. See
Default Version of a Resource and
Versions for more details.
3 - It has a set of attributes for Resource level metadata - data that is not
specific to one Version of the Resource but instead applies to the
Resource in general. These attributes appear under a meta
attribute/sub-object so as to keep them separate from any Version level
attributes. Note that these attributes do not appear on the Versions.
The URL of a Resource can be thought of as an alias for the "default" Version of the Resource, and as such, most of the attributes shown when processing the Resource will be mapped from the "default" Version.
However, there a few exceptions:
self
MUST be a URL to the Resource, and not to the "default"
Version in the versions
collection. The Resource's defaultversionurl
attribute (in the meta
sub-object) can be used to locate the "default"
Version.shortself
, if present, MUST be an alternative URL for self
.xid
MUST be a relative URI to the Resource, and not to the "default"
Version in the versions
collection.meta
sub-object and the versions
Collection attributes.The remainder of this section discusses the processing rules for Resources and Versions. While it mainly uses the term "Resource" for ease of reading, in most cases it can be assumed that the same applies for "Versions". When this is not the case, it will be explicitly called out.
Unlike Groups, which consist entirely of xRegistry managed metadata, Resources
typically have their own domain-specific data and document format that needs
to be kept distinct from the xRegistry Resource metadata. As discussed
previously, the model definition for Resource has a hasdocument
attribute
indicating whether a Resource type defines its own separate document or not.
This specification does not define any requirements for the contents of this separate document, and it doesn't even need to be stored within the Registry. The Resource MAY choose to simply store a URL reference to the externally managed document instead. When the document is stored within the Registry, it can be managed as an opaque array of bytes.
When a Resource does have a separate document, HTTP interactions to the URL for the Resource MUST include this document in the HTTP body as it is typically the data of interest for end users. As a convenance, the simple (mainly scalar) xRegistry metadata of the Resource will appear as HTTP headers.
To change this view such that the xRegistry metadata becomes the data of
interest, the request URLs MUST have $details
appended to them. In these
cases, the HTTP body of the requests and responses MUST have a JSON
serialization of the entity's xRegistry metadata, and the separate document
MAY appear as an attribute within that metadata based on the specific
operation being done.
For example:
GET https://example.com/schemagroups/mygroup/schemas/myschema
will retrieve the schema document associated with the myschema
Resource,
while:
GET https://example.com/schemagroups/mygroup/schemas/myschema$details
will retrieve the xRegistry metadata information for the myschema
Resource.
When the Resource's path is appended with $details
, the Resource's document
becomes available via a set of RESOURCE*
attributes within that metadata:
RESOURCE
: this attribute MUST be used when the contents of the Resource's
document are stored within the Registry and its "array of bytes" can be
used directly in the serialization of the Resource metadata "as is". Meaning,
in the JSON case, those bytes can be parsed as a JSON value without any
additional processing (such as escaping) being done. This is a convenience
(optimization) attribute to make it easier to view the document when it
happens to be in the same format as the xRegistry itself.
The model Resource attribute typemap
MAY be used to help the server
determine which contenttype
values are of the same format - see
Registry Model for more information. If a Resource has
a matching contenttype
but the contents of the Resource's document do not
successfully parse (e.g. it's application/json
but the JSON is invalid),
then RESOURCE
MUST NOT be used and RESOURCEbase64
MUST be used instead.
RESOURCEbase64
: this attribute MUST be used when the contents of the
Resource's are stored within the Registry but RESOURCE
can not be used.
The Resource's document is base64 encoded and serialized as a string.
RESOURCEurl
: this attribute MUST be used when the Resource is stored
external to the Registry and its value MUST be a URL that can be used to
retrieve its contents via an HTTP(s) GET
.
When accessing a Resource's metadata with $details
, often it is to
view or update the xRegistry metadata and not the document, as such, including
the potentially large amount of data from the Resource's document in request
and response messages could be cumbersome. To address this, the RESOURCE
and
RESOURCEbase64
attributes do not appear by default as part of the
serialization of the Resource. Rather, they MUST only appear in responses when
the ?inline=RESOURCE
query parameter is used. Likewise, in
requests, these attributes are OPTIONAL and would only need to be used when a
change to the document's content is needed at the same time as updates to the
Resource's metadata. However, the RESOURCEurl
attribute MUST always appear
if it has a value.
Note that the serialization of a Resource MUST only use at most one of these 3 attributes at a time.
Resource attributes are non-versioned attributes associated with a Resource. In a sense they can be considered to be global to the Resource and its Versions, but they are not part of, or serialized in, any Version. Instead, they are serialized in two different ways:
1 - some will appear within a meta
attribute/sub-object to the Resource.
This keeps them separate from the default Version attributes that might
appear. However, the meta
attribute itself will appear as a sibling to
the default Version attributes. Note that meta
will only be
serialized when requested by the client.
2 - some will appear as siblings to the default Version attributes within the
Resource serialization. These appear here, rather than under meta
,
because they are specifically designed to help with the traversal of the
Resource's hierarchy and putting them "one level down" would reduce their
usefulness.
When the Resource is serialized as a JSON object, the serialization of the Resource attribute MUST adhere to the following:
{
"RESOURCEid": "STRING",
"versionid": "STRING",
"self": "URL", # URL to Resource, not Version
"shortself": "URL", ?
"xid": "XID", # Relative URI to Resource
# Default Version attributes appear here
"metaurl": "URL",
"meta": { # Only if inlined
"RESOURCEid": "STRING",
"self": "URL", # Absolute Meta URL, not Version
"shortself": "URL", ?
"xid": "XID", # Relative Meta URI, not Version
"xref": "URL", ? # Ptr to linked Resource
"epoch": UINTEGER, # Resource's epoch
"createdat": "TIMESTAMP", # Resource's
"modifiedat": "TIMESTAMP", # Resource's
"readonly": BOOLEAN, # Default=false
"compatibility": "STRING", # Default=none
"compatibilityauthority": "STRING", ? # Default=external
"deprecated": { ... }, ?
"defaultversionid": "STRING",
"defaultversionurl": "URL",
"defaultversionsticky": BOOLEAN # Default=false
}, ?
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { map of Versions } # Only if inlined
}
Note that the meta
and versions
attributes MUST only appear when
requested by the client - for example, via the ?inline
flag.
When the Resource is serialized with its domain-specific document in the HTTP body, then Resource level attributes SHOULD appear as HTTP headers and adhere to the following:
xRegistry-RESOURCEid: STRING
xRegistry-versionid: STRING
xRegistry-self: URL
xRegistry-xid: URI
# Default Version attributes, and other HTTP headers, appear here
xRegistry-metaurl: URL
xRegistry-versionsurl: URL
xRegistry-versionscount: UINTEGER
Notice the meta
and versions
attributes are not included since they are
not complex data types.
The Resource level attributes include the following common attributes:
RESOURCEid
- REQUIRED in API and document
views. OPTIONAL in requests.self
- REQUIRED in API and document view. OPTIONAL in
requests.shortself
- OPTIONAL in API and document views,
based on the shortself
capability. OPTIONAL in requests.xid
- REQUIRED in API and document views. OPTIONAL in
requests.and the following Resource level attributes:
xref
AttributeType: Relative URI (xid).
Description: indicates that this Resource is a reference to another Resource within the same Registry. See Cross Referencing Resources for more information.
Constraints:
xid
of a same-typed Resource in the Registry.readonly
AttributeType: Boolean
Description: indicates whether this Resource is updateable by clients. This attribute is a server controlled attribute and therefore cannot be modified by clients. This specification makes no statement as to when Resources are to be read-only.
Constraints:
false
.true
or false
.?noreadonly
query parameter was used,
in which case the error MUST be silently ignored. See
Registry APIs for more information.compatibility
AttributeType: String (with resource-specified enumeration of possible values)
Description: States that Versions of this Resource adhere to a certain
compatibility rule. For example, a "backward" compatibility value would
indicate that all Versions of a Resource are backwards compatible with the
next oldest Version, as determined by their ancestor
attributes.
This specification makes no statement as to which parts of the data are
examined for compatibility (e.g. xRegistry metadata, domain-specific
document, etc.). This SHOULD be defined by the compatibility
values.
The exact meaning of what each compatibility
value means might vary based
on the data model of the Resource, therefore this specification only defines
a very high-level abstract meaning for each to ensure some degree of
consistency. However, domain-specific specifications are expected to
modify the compatibility
enum values defined in the Resource's model to
limit the list of available values and to define the exact meaning of each.
Implementations MUST include none
as one of the possible values and when
set to none
then compatibility checking MUST NOT be performed.
If the compatibilityauthority
attribute is set to server
then
implementations of this specification are REQUIRED to perform the proper
compatibility checks to ensure that all Versions of a Resource adhere to the
rules defined by the current value of this attribute.
For compatibility
strategies that require understanding the sequence in
which Versions need to be compatible, the server MUST use the
ancestor
to determine the sequence of Versions.
Note that, like all attributes, if a default value is defined as part of the model, then this attribute MUST be populated with that value if no value is provided.
This specification defines the following enumeration values. Implementations MAY choose to extend this list, or use a subset of it.
backward
- A Version is compatible with the next oldest Version.backward_transitive
- A Version is compatible with all older Versions.forward
- A Version is compatible with the next newest Version.forward_transitive
- A Version is compatible with all newer Versions.full
- A Version is compatible with the next oldest and next newest
Versions.full_transitive
- A Version is compatible with all older and all newer
Versions.none
- No compatibility checking is performed.Constraints:
none
.none
as a valid value.compatibilityauthority
attribute is set to server
, when
changing the compatibility
attribute, the server MUST validate whether
the new compatibility
value can be enforced across all existing
Versions. If that's not the case, the server MUST generate an error
(compatibility_violation).compatibilityauthority
AttributeName: compatibilityauthority
Type: String
Description: Indicates the authority that enforces the compatibility
value defined by the owning Resource.
This specification defines the following enumeration values. Implementations MAY choose to extend this list.
external
- The compatibility is enforced by an external authority.server
- The compatibility is enforced by the server.The server MUST generate an error
(compatibility_violation) following any
attempt to set the compatibilityauthority
attribute to server
if the
server cannot enforce the compatibility for the Resource's Versions.
When set to server
, the server MUST generate an error
(compatibility_violation) following any
attempt to create/update a Resource (or its Versions) that would result in
those entities violating the stated compatibility statement.
A value of external
indicates that the server MUST NOT perform any
compatibility checking and that the compatibility checking is performed by
an external authority.
Attempts to change this value to server
MUST result in the validation of
existing Versions.
Constraints:
compatibility
is not none
.compatibility
is none
.compatibility
is not none
, the default value
MUST be external
.deprecated
Type: Object containing the following properties:
effective
An OPTIONAL property indicating the time when the Resource entered, or will
enter, a deprecated state. The date MAY be in the past or future. If this
property is not present the Resource is already in a deprecated state.
If present, this MUST be an RFC3339 timestamp.
removal
An OPTIONAL property indicating the time when the Resource MAY be removed.
The Resource MUST NOT be removed before this time. If this property is not
present then client can not make any assumption as to when the Resource
might be removed. Note: as with most properties, this property is mutable.
If present, this MUST be an RFC3339 timestamp and MUST NOT be
sooner than the effective
time if present.
alternative
An OPTIONAL property specifying the URL to an alternative Resource the
client can consider as a replacement for this Resource. There is no
guarantee that the referenced Resource is an exact replacement, rather the
client is expected to investigate the Resource to determine if it is
appropriate.
docs
An OPTIONAL property specifying the URL to additional information about
the deprecation of the Resource. This specification does not mandate any
particular format or information, however some possibilities include:
reasons for the deprecation or additional information about likely
alternative Resources. The URL MUST support an HTTP GET request.
Note that an implementation is not mandated to use this attribute in advance of removing a Resource, but is it RECOMMENDED that they do so.
Constraints:
Examples:
"deprecated": {}
"deprecated": {
"removal": "2030-12-19T00:00:00",
"alternative": "https://example.com/entities-v2/myentity"
}
defaultversionid
AttributeType: String
Description: the versionid
of the default Version of the Resource.
This specification makes no statement as to the format of this string or
versioning scheme used by implementations of this specification. However, it
is assumed that newer Versions of a Resource will have a "higher"
value than older Versions.
Constraints:
versionid
of the default Version of the Resource.defaultversionsticky
Attribute
below for how to process these two attributes.Examples:
1
, 2.0
, v3-rc1
(v3's release candidate 1)defaultversionurl
AttributeType: URL
Description: a URL to the default Version of the Resource.
This URL MUST NOT include the $detail
suffix even if the Resource's
hasdocument
aspect is set to true
.
API View Constraints:
self
attribute.Document View Constraints:
#JSON-POINTER
where the JSON-POINTER
locates the default Version within the current document. See
Doc Flag for more information.Examples:
https://example.com/endpoints/ep1/messages/msg1/versions/1.0
defaultversionsticky
AttributeType: Boolean
Description: indicates whether or not the "default" Version has been
explicitly set or whether the "default" Version is always the newest one
based on the createdat
timestamp. A value of true
means that it has been
explicitly set and the value of defaultversionid
MUST NOT automatically
change if Versions are added or removed. A value of false
means the default
Version MUST be the newest Version based on createdat
timestamps.
When set to true
, if the default Version is deleted, then without any
indication of which Version is to become the new default Version, the
sticky aspect MUST be disabled and the default Version MUST be the newest
Version. See Default Version of a Resource
for more information.
Constraints:
false
.true
or false
.null
MUST be interpreted as a
request to delete the attribute, implicitly setting it to false
.defaultversionsticky
and defaultversionid
attributes are related, and is described as follows:
PATCH
is used but only one of the two attributes is specified
in the request, then:
null
defaultversionid
MUST result in processing as if
defaultversionsticky
has a value of true
.null
defaultversionid
MUST result in processing as if
defaultversionsticky
has a value of false
.null
or false
defaultversionsticky
MUST result in processing
as if defaultversionid
has a value of null
.meta
sub-object as if
PUT
or POST
was used.PUT
or POST
is used:
null
or absent defaultversionid
in the request MUST result in the
same semantics as it referencing "the newest Version".null
or absent defaultversionsticky
in the request MUST result in
the same semantics as it being set to false
.defaultversionid
referencing a non-existing Version MUST generate
an error (unknown_id).defaultversionsticky
is false
and defaultversionid
does not
reference the newest Version then an error
(invalid_data) MUST be generated, as this
would result in an inconsistent state.defaultversionid
MUST reference the
newest Version.Examples:
true
, false
meta
Attribute/Sub-ObjectType: Object
Description: an object that contains most of the Resource level attributes.
The meta
sub-object is an entity in its own right, meaning it supports the
GET
, PUT
and PATCH
APIs as described for all entities within the
xRegistry. It also has its own epoch
value which adheres to the normal
epoch
processing rules already described, and its value is only updated
when the meta
attributes are updated.
When specified, it MUST appear as an attribute of the Resource as a sibling to the Resource's default Version attributes.
API View Constraints:
Document View Constraints:
Note: doing a PUT
to a Resource, or a POST
to an xRegistry Collection, as
a mechanism to update the meta
sub-object MUST include the Resource
default Version attributes in the request. When not specified, the server will
interpret it as a request to delete the default Version attributes. If
possible, an update request to the metaurl
directly would be a better
choice, or use PATCH
instead and only include the meta
sub-object.
During a write operation, the absence of the meta
attribute indicates that
no changes are to be made to the meta
sub-object.
metaurl
AttributeType: URL
Description: a server generated URL to the Resource's meta
sub-object.
When specified, it MUST appear as an attribute of the Resource as a sibling to the Resource's default Version attributes.
API View Constraints:
meta
sub-object.Document View Constraints:
meta
sub-object is inlined in the document then this attribute
MUST be a relative URL of the form #JSON-POINTER
where the JSON-POINTER
locates the meta
sub-object within the current document. See
Doc Flag for more information.meta
sub-object is not inlined in the document then this attribute
MUST be an absolute URL per the API view constraints listed above.Examples:
https://example.com/endpoints/ep1/messages/msg1/meta
versions
CollectionType: Registry Collection
Description: A map of Versions of the Resource.
Note that Resources MUST have at least one Version.
Constraints:
Serializing Resources requires some special processing due to Resources not representing just a single set of data. In particular, the following aspects need to be taken into account:
To address these aspects, the serialization of a Resource will vary based on whether it is defined to have a domain-specific document and whether the client wishes to focus on managing its xRegistry metadata or that secondary document.
As discussed above, there are two ways to serialize a Resource in an HTTP message's body:
Which variant is used is controlled by the use of $details
on the URL path.
The following sections go into more details about these two serialization
options.
When a Resource is serialized as its underlying domain-specific document,
in other words $details
is not appended to its URL path, the HTTP body of
requests and responses MUST be the exact bytes of the document. If the
document is empty, or there is no document, then the HTTP body MUST be empty
(zero length).
In this serialization mode, it might be useful for clients to have access to Resource's xRegistry metadata. To support this, some of the Resource's xRegistry metadata will appear as HTTP headers in response messages.
On responses, unless otherwise stated, all top-level scalar attributes of the
Resource SHOULD appear as HTTP headers where the header name is the name of the
attribute prefixed with xRegistry-
. Note, the optionality of this requirement
is not to allow for servers to decide whether or not to do so, rather it is to
allow for No-Code Servers servers than might not be able
to control the HTTP response headers.
Certain attributes do not follow this rule if a standard HTTP header name is
to be used instead (e.g. contenttype
MUST use Content-Type
, not
xRegistry-contenttype
). Each attribute that falls into this category will
be identified as part of its definition.
Top-level map attributes whose values are of scalar types MUST also appear as
HTTP headers (each key having its own HTTP header) and in those cases the
HTTP header names will be of the form: xRegistry-ATTRIBUTENAME-KEYNAME
. Note
that map keys MAY contain the -
character, so any -
after the 2nd -
is
part of the key name. See
HTTP Header Values for additional information and
labels
for an example of one such attribute.
Complex top-level attributes (e.g. arrays, objects, non-scalar maps) MUST NOT appear as HTTP headers.
On update requests, similar serialization rules apply. However, rather than
these headers being REQUIRED, the client would only need to include those
top-level attributes that they would like to change. But, including unchanged
attributes MAY be done. Any attributes not included in request messages
MUST be interpreted as a request to leave their values unchanged. Using a
value of null
(case-sensitive) indicates a request to delete that attribute.
Any top-level map attributes that appear as HTTP headers MUST be included in their entirety and any missing keys MUST be interpreted as a request to delete those keys from the map.
Since only some types of attributes can appear as HTTP headers, $details
MUST be used to manage the others. See the next section for more details.
When a Resource (not a Version) is serialized with the Resource document in the HTTP body, it MUST adhere to this form:
Content-Type: STRING ?
xRegistry-RESOURCEid: STRING # ID of Resource, not default Version
xRegistry-versionid: STRING # ID of the default Version
xRegistry-self: URL # Resource URL, not default Version
xRegistry-xid: URI # Relative Resource URI
xRegistry-epoch: UINT # Start default Version's attributes
xRegistry-name: STRING ?
xRegistry-isdefault: true
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
xRegistry-ancestor: STRING
xRegistry-RESOURCEurl: URL ? # End of default Version attributes
xRegistry-metaurl: URL # Resource level attributes
xRegistry-versionsurl: URL
xRegistry-versionscount: UINTEGER
Location: URL
Content-Location: URL ?
Content-Disposition: STRING ?
... Resource document ... ?
Where:
RESOURCEid
is the SINGULARid
of the Resource, not the default
Version.versionid
attribute MUST be the ID of the Resource's default Version.xid
URI references the Resource, not the default Version.versionsurl
and versionscount
Collection attributes are included,
but not the versions
collection itself.Location
header only appears in response to a create operation and
MUST be the same as the self
URL.Content-Location
header MAY appear, and if present, MUST reference
the "default" Version.Content-Disposition
SHOULD be present and if so, MUST be the RESOURCEid
value. This allows for HTTP tooling that is not aware of xRegistry to know
the desired filename to use if the HTTP body were to be written to a file.Version serialization will look similar, but the set of xRegistry HTTP headers will be slightly different (to exclude Resource level attributes). See the next sections for more information.
Scalar default Version extension attributes MUST also appear as
xRegistry-
HTTP headers.
Appending $details
to a Resource or Version's URL path modifies the
serialization of the entity such that rather than the HTTP body containing
the entity's domain-specific "document" and the xRegistry metadata being
in HTTP headers, all of them are instead within the HTTP body as one JSON
object. If the entity's "document" is included within the object then it'll
appear under a RESOURCE*
attribute (as discussed above).
The advantage of this format is that the HTTP body will contain all of the xRegistry metadata and not just the scalar values - as is the case when they appear as HTTP headers. This allows for management of all metadata as well as any possible domain-specific document at one time.
Note that in the case of a reference to a Resource (not a Version), the
metadata will be from the default Version, plus the extra meta
and versions
related attributes.
When serialized as a JSON object, a Resource (not a Version) MUST adhere to this form:
{
"RESOURCEid": "STRING", # ID of Resource, not default Version
"versionid": "STRING", # ID of default Version
"self": "URL", # URL of Resource,not default Version
"shortself": "URL", ?
"xid": "XID", # Relative URI of Resource
# These are inherited from the default Version
"epoch": UINTEGER,
"name": "STRING", ?
"isdefault": true,
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"ancestor": "STRING",
"contenttype": "STRING", ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If inlined & JSON
"RESOURCEbase64": "STRING", ? # If inlined & ~JSON
# Resource level helper attributes
"metaurl": "URL",
"meta": { # If inlined
"RESOURCEid": "STRING",
"self": "URL", # URL to "meta"
"shortself": "URL", ?
"xid": "XID", # Relative URI to "meta"
"xref": "URL", ? # Ptr to linked Resource
"epoch": UINTEGER, # Resource's epoch
"createdat": "TIMESTAMP", # Resource's
"modifiedat": "TIMESTAMP", # Resource's
"readonly": BOOLEAN, # Default=false
"compatibility": "STRING", # Default=none
"compatibilityauthority": "STRING", ? # Default=external
"deprecated": { ... },
"defaultversionid": "STRING",
"defaultversionurl": "URL",
"defaultversionsticky": BOOLEAN # Default=false
}, ?
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { Versions collection } ? # If inlined
}
The serialization of a Version will look similar except the meta
and
versions
related Resource level attributes MUST NOT be present. More on this
in the next sections.
Typically, Resources exist within the scope of a single Group, however there might be situations where a Resource needs to be related to multiple Groups. In these cases, there are two options. First, a copy of the Resource could be made into the second Group. The obvious downside to this is that there's no relationship between the two Resources and any changes to one would need to be done in the other - running the risk of them getting out of sync.
The second, and better, option is to create a cross-reference from one
(the "source" Resource) to the other ("target" Resource). This is done
by setting the xref
attribute on the source Resource to be the xid
of the target Resource.
The xref
attribute is defined in the model as:
"xref": {
"name": "xref",
"type": "xid",
"target": "/GROUPS/RESOURCES"
}
where /GROUPS/RESOURCES
will be the actual Group and Resource plural names
of this Resource.
Looking at a specific example, a Group/Resource model definition of:
{
"groups" : {
"schemagroups": {
"plural": "schemagroups",
"singular": "schemagroup",
"resources": {
"plural": "schemas",
"singular": "schema",
"attributes": {
"xref": {
"name": "xref",
"type": "xid",
"target": "/schemagroups/schemas"
}
}
}
}
}
}
Means that schemas
can be cross-referenced to other /schemagroups/schema
Resource. Notice that target
is a xid
template to itself.
For example: a schema
Resource instance defined as (HTTP body of
PUT /schemagroups/group1/schemas/mySchema$details
):
{
"schemaid": "mySchema",
"meta": {
"xref": "/schemagroups/group2/schemas/sharedSchema"
}
}
means that mySchema
references sharedSchema
, which exists in group2
.
When this source Resource (mySchema
) is retrieved, all of the target
Resource's attributes (except its RESOURCEid
) will appear as if they were
locally defined.
So, if the target Resource (sharedSchema
) is defined as:
{
"resourceid": "sharedSchema",
"versionid": "v1",
"self": "http://example.com/schemagroups/group2/schemas/sharedSchema",
"xid": "/schemagroups/group2/schemas/sharedSchema",
"epoch": 2,
"isdefault": true,
"createdat": "2024-01-01-T12:00:00",
"modifiedat": "2024-01-01-T12:01:00",
"ancestor": "v1",
"metaurl": "http://example.com/schemagroups/group2/schemas/sharedSchema/meta",
"versionscount": 1,
"versionsurl": "http://example.com/schemagroups/group2/schemas/sharedSchema/versions"
}
then the resulting serialization of the source Resource would be:
{
"resourceid": "mySchema",
"versionid": "v1",
"self": "http://example.com/schemagroups/group1/schemas/mySchema",
"xid": "/schemagroups/group1/schemas/mySchema",
"epoch": 2,
"isdefault": true,
"createdat": "2024-01-01-T12:00:00",
"modifiedat": "2024-01-01-T12:01:00",
"ancestor": "v1",
"metaurl": "http://example.com/schemagroups/group1/schemas/mySchema/meta",
"meta": {
"resourceid": "mySchema",
"self": "http://example.com/schemagroups/group1/schemas/mySchema/meta",
"xid": "/schemagroups/group1/schemas/mySchema/meta",
"xref": "/schemagroups/group2/schemas/sharedSchema",
"createdat": "2024-01-01-T12:00:00",
"modifiedat": "2024-01-01-T12:01:00",
"readonly": false,
"compatibility": "none"
},
"versionscount": 1,
"versionsurl": "http://example.com/schemagroups/group1/schemas/mySchema/versions"
}
Note:
xref
attribute.
For example, its RESOURCEid
MUST be the source's id
and not the target's.xref
attribute MUST appear within the meta
sub-object so a client
can easily determine that this Resource is a cross-referenced Resource, and
it provides a reference to the targeted Resource.xref
URL MUST be the xid
of the target Resource.From a consumption (read) perspective, aside from the presence of the xref
attribute, the Resource appears to be a normal Resource that exists within
group1
. All of the specification-defined features (e.g. ?inline
,
?filter
) MAY be used when retrieving the Resource.
However, from a write perspective it is quite different. In order to update the target Resource's attributes (or nested entities), a write operation MUST be done on the appropriate target Resource entity directly. Write operations on the source MAY be done, however, the changes are limited to converting it from a "cross-reference" Resource back into a "normal" Resource. See the following for more information:
When converting a "normal" Resource into a cross-reference Resource (adding
an xref
value), or creating a new Resource that will be a cross-reference
Resource, the following MUST be adhered to:
xref
attribute with a value that is
the xid
of the target Resource.RESOURCEid
attribute (of the source
Resource) on the Resource or meta
sub-object.epoch
within meta
(to do an epoch
validation
check) only if the Resource already exists.When converting a cross-reference Resource back into a "normal" Resource, the following MUST be adhered to:
xref
attribute or set it to null
.createdat
value MUST be
reset to the timestamp of when this source Resource was originally created.modifiedat
value MUST be
set to "now".epoch
value (in meta
) MUST be greater than the original
Resource's previously known value (if any) and greater than the target
Resource's epoch
value. In pseudocode this is:
epoch = max(original_Epoch, target_Resource_Epoch) + 1
.
This will ensure that the Resource's epoch
value will never decrease as a
result of this operation. Note that going from a "normal" Resource to a
cross-reference Resource does not have this guarantee. If the target Resource
no longer exists then target_Resource_Epoch
MUST be treated as zero.If the target Resource itself is a cross-reference Resource, then including
the target Resource's attributes MUST NOT be done when serializing the
source Resource. Recursive, or transitively, following of xref
URLs is not
done.
Both the source and target Resources MUST be of the same Resource model type,
simply having similar Resource type definitions is not sufficient. This
implies that use of the ximport
feature in the model to reference a
Resource type from another Group type definition MUST be used if the same
Resource type is to exist in different Group types. See
ximport
for more information.
An xref
value that points to a non-existing Resource, either because
it was deleted or never existed, is not an error and is not a condition
that a server is REQUIRED to detect. In these "dangling xref" situations, the
serialization of the source Resource will not include any target Resource
attributes or nested collections. Rather, it will only show the RESOURCEid
and xref
attributes.
For convenience, the Resource and Version create, update and delete APIs can be summarized as:
POST /GROUPS/gID
POST /GROUPS/gID/RESOURCES
PATCH /GROUPS/gID/RESOURCES
PUT /GROUPS/gID/RESOURCES/rID[$details]
PATCH /GROUPS/gID/RESOURCES/rID[$details]
POST /GROUPS/gID/RESOURCES/rID[$details]
PATCH /GROUPS/gID/RESOURCES/rID[$details]
PUT /GROUPS/gID/RESOURCES/rID/meta
PATCH /GROUPS/gID/RESOURCES/rID/meta
meta
sub-object of a Resource.POST /GROUPS/gID/RESOURCES/rID/versions
PATCH /GROUPS/gID/RESOURCES/rID/versions
PUT /GROUPS/gID/RESOURCES/rID/versions/vID[$details]
PATCH /GROUPS/gID/RESOURCES/rID/versions/vID[$details]
And the delete APIs are summarized as:
DELETE /GROUPS/gID/RESOURCES
DELETE /GROUPS/gID/RESOURCES/rID
DELETE /GROUPS/gID/RESOURCES/rID/versions
DELETE /GROUPS/gID/RESOURCES/rID/versions/vID
The following sections go into more detail about each API.
To retrieve all Resources in a Resource Collection, an HTTP GET
MAY be used.
The request MUST be of the form:
GET /GROUPS/gID/RESOURCES
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <URL>;rel=next;count=UINTEGER ?
{
"KEY": { # The Resource ID
"RESOURCEid": "STRING", # The Resource ID
"versionid": "STRING", # Default Version ID
"self": "URL", # URL to the Resource
"shortself": "URL", ?
"xid": "XID", # Relative URI to the Resource
"epoch": UINTEGER, # Start of Default Ver attribs
"name": "STRING", ?
"isdefault": true,
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"ancestor": "STRING",
"contenttype": "STRING", ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If inlined & JSON
"RESOURCEbase64": "STRING", ? # If inlined & ~JSON
# Resource level helper attributes
"metaurl": "URL",
"meta": { # If inlined
"RESOURCEid": "STRING", # Resource ID
"self": "URL", # URL to "meta"
"shortself": "URL", ?
"xid": "XID", # Relative URI to "meta"
"xref": "URL", ? # Ptr to linked Resource
"epoch": UINTEGER, # Resource's epoch
"createdat": "TIMESTAMP", # Resource's
"modifiedat": "TIMESTAMP", # Resource's
"readonly": BOOLEAN, # Default=false
"compatibility": "STRING", # Default=none
"compatibilityauthority": "STRING", ? # Default=external
"deprecated": { ... }, ?
"defaultversionid": "STRING",
"defaultversionurl": "URL",
"defaultversionsticky": BOOLEAN
}
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { Versions collection } ? # If inlined
} *
}
Where:
RESOURCEid
of the respective
Resource.Examples:
Retrieve all messages
of an endpoint
whose RESOURCEid
is ep1
:
GET /endpoints/ep1/messages
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <https://example.com/endpoints/ep1/messages&page=2>;rel=next;count=100
{
"msg1": {
"messageid": "msg1",
"versionid": "1.0",
"self": "https://example.com/endpoints/ep1/messages/msg1",
"xid": "/endpoints/ep1/messages/msg1",
"epoch": 1,
"name": "Blob Created",
"isdefault": true,
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"ancestor": "1.0",
"metaurl": "https://example.com/endpoints/ep1/messages/msg1/meta",
"versionsurl": "https://example.com/endpoints/ep1/messages/msg1/versions",
"versionscount": 1
}
}
These APIs follow the overall pattern described in the Creating or Updating Entities section. Any variations will be called out.
Creating and updating of Resources via HTTP MAY be done using the HTTP POST
,
PUT
or PATCH
methods as described below:
POST /GROUPS/gID
Where:
For example:
{
"schemas": {
"schema1": { ... Resource schema1's xRegistry metadata ... },
"schema2": { ... Resource schema1's xRegistry metadata ... }
},
"messages": {
"message1": { ... Resource message1's xRegistry metadata ... },
"message2": { ... Resource message2's xRegistry metadata ... }
}
}
Notice the format is almost the same as what a PUT /GROUPS/gID
would look
like if the request wanted to update the Group's attributes and define a set
of Resources, but without the Group's attributes. This allows for an update of
the specified Resources without modifying the Group's attributes.
The response in this case MUST be a map of the Resource types with just the Resources that were processed as part of the request.
POST /GROUPS/gID/RESOURCES
PATCH /GROUPS/gID/RESOURCES
Where:
PUT /GROUPS/gID/RESOURCES/rID[$details]
PATCH /GROUPS/gID/RESOURCES/rID[$details]
Where:
$details
is present, the HTTP body MUST be an xRegistry
serialization of the Resource.$details
is absent, the HTTP body MUST contain the Resource's
document (an empty body means the document is to be empty).POST /GROUPS/gID/RESOURCES/rID[$details][?setdefaultversionid=vID]
PATCH /GROUPS/gID/RESOURCES/rID[$details][?setdefaultversionid=vID]
Where:
hasdocument
aspect set to true
:
$details
is present in the URL, then the HTTP body MUST be an
xRegistry serialization of the Version that is to be created or updated.$details
is absent in the URL, then the HTTP body MUST contain
the Version's document (an empty body means the document is to be empty).
Note that the xRegistry metadata (e.g. the Version's versionid
) MAY be
included as HTTP headers.hasdocument
aspect set to false
then the
HTTP body MUST be an xRegistry serialization of the Version that is to be
created or updated.PUT /GROUPS/gID/RESOURCES/rID/meta
PATCH /GROUPS/gID/RESOURCES/rID/meta
Where:
meta
sub-object of the specified Resource.epoch
value that does not match the existing meta
epoch
value then an
error (mismatched_epoch) MUST be generated.POST /GROUPS/gID/RESOURCES/rID/versions[?setdefaultversionid=vID]
PATCH /GROUPS/gID/RESOURCES/rID/versions[?setdefaultversionid=vID]
Where:
versionid
not the Resource's.?setdefaultversionid
query parameter is OPTIONAL.?setdefaultversionid
query parameter is RECOMMENDED.See Default Version of a Resource for more
information about the ?setdefaultversionid
query parameter.
PUT /GROUPS/gID/RESOURCES/rID/versions/vID[$details][?setdefaultversionid=vID]
PATCH /GROUPS/gID/RESOURCES/rID/versions/vID[$details][?setdefaultversionid=vID]
Where:
hasdocument
aspect set to true
:
$details
is present in the URL, then the HTTP body MUST be an
xRegistry serialization of the Version that is to be created or updated.$details
is absent in the URL, then the HTTP body MUST contain
the Version's document (an empty body means the document is to be empty).
Note that the xRegistry metadata (e.g. the Version's versionid
) MAY be
included as HTTP headers.hasdocument
aspect set to false
then the
HTTP body MUST be an xRegistry serialization of the Version that is to be
created or updated.See Default Version of a Resource for more
information about the ?setdefaultversionid
query parameter.
To reduce the number of interactions needed, these APIs are designed to allow
for the implicit creation of all parent entities specified in the PATH. And
each entity not already present with the specified SINGULARid
MUST be
created with that value. Note: if any of those entities have REQUIRED
attributes, then they cannot be implicitly created, and would need to be
created directly.
When specified as an xRegistry JSON object, each individual Resource or Version in the request MUST adhere to the following:
{
"RESOURCEid": "STRING", ?
"versionid": "STRING", ?
"epoch": UINTEGER,
"name": "STRING", ? # Version-level attributes
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP", ?
"modifiedat": "TIMESTAMP", ?
"ancestor": "STRING",
"contenttype": "STRING", ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If inlined & JSON
"RESOURCEbase64": "STRING", ? # If inlined & ~JSON
"meta": { # Resource-only attributes
"RESOURCEid": "STRING", ?
"xref": "URL", ?
"epoch": UINTEGER, ?
"createdat": "TIMESTAMP", ?
"modifiedat": "TIMESTAMP", ?
"compatibility": "STRING", ?
"compatibilityauthority": "STRING", ?
"deprecated": { ... }, ?
"defaultversionid": "STRING",
"defaultversionsticky": BOOLEAN
}, ?
"versions": { Versions collection } ?
}
When the HTTP body contains the Resource's (or Version's) document, then any xRegistry scalar metadata MUST appear as HTTP headers and the request MUST adhere to the following:
[METHOD] [PATH]
Content-Type: STRING ?
xRegistry-RESOURCEid: STRING ?
xRegistry-versionid: STRING ?
xRegistry-epoch: UINTEGER ?
xRegistry-name: STRING ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-createdat: TIMESTAMP ?
xRegistry-modifiedat: TIMESTAMP ?
xRegistry-ancestor: STRING ?
xRegistry-RESOURCEurl: URL ?
... entity document ... ?
Where:
In the cases where xRegistry metadata appears as HTTP headers, if the
RESOURCEurl
attribute is present with a non-null value, the HTTP body
MUST be empty. If the RESOURCEurl
attribute is absent, then the contents
of the HTTP body (even if empty) are to be used as the entity's document.
If the Resource's hasdocument
model attribute has a value of false
then
the following rules apply:
$details
suffix on the request URL is OPTIONAL, and if used
the Resource/Version self
URL MUST NOT include $details
.If the versionid
attribute is present, but it does not match the existing
"default" Version's versionid
(after any necessary processing of the
defaultversionid
attribute), then an error
(mismatched_id) MUST be generated. Also see
Default Version of a Resource.
If the versionid
attribute is present while creating a new Resource, but
a versions
collection is not included, rather than the server generating
the versionid
of the newly created "default" Version, the server MUST use
the passed-in versionid
attribute value. This is done as a convenience
for clients to avoid them having to include a versions
collection just
to set the initial default Version's SINGULARid
. In other words, when
the versions
collection is absent on a create, but versionid
is
present, there is an implied "versions": { "vID": {} }
(where vID
is the versionid
value).
When the xRegistry metadata is serialized as a JSON object, the processing
of the 3 RESOURCE
attributes MUST follow these rules:
RESOURCEurl
),
then absence of all 3 attributes MUST leave all 3 unchanged.null
for any of the 3 attributes MUST delete all
3 attributes (and any associated data).RESOURCE
is present, the server MAY choose to modify non-semantic
significant characters. For example, to remove (or add) whitespace. In
other words, there is no requirement for the server to persist the
document in the exact byte-for-byte format in which it was provided. If
that is desired then RESOURCEbase64
MUST be used instead.PUT
or POST
, when RESOURCE
is present, if no contenttype
value is provided then the server MUST set it to same type as the incoming
request, e.g. application/json
, even if the entity previous had a
contenttype
value.PATCH
, when RESOURCE
or RESOURCEbase64
is present, if no
contenttype
value is provided then the server MUST set it to the same
type as the incoming request, e.g. application/json
, only if the entity
does not already have a value. Otherwise, the existing value remains
unchanged.A successful response MUST include the current representation of the entities
created or updated and be in the same format ($details
variant or not) as
the request.
If the request used the PUT
or PATCH
variants directed at a single entity,
and a new Version was created, then a successful response MUST include a
Content-Location
HTTP header to the newly created Version entity, and if
present, it MUST be the same as the Version's self
attribute.
Note that the response MUST NOT include any inlinable attributes (such as
RESOURCE
, RESOURCEbase64
or nested objects/collections).
Examples:
Create a new Resource:
PUT /endpoints/ep1/messages/msg1
Content-Type: application/json; charset=utf-8
xRegistry-name: Blob Created
{
# Definition of a "Blob Created" event (document) excluded for brevity
}
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
xRegistry-messageid: msg1
xRegistry-versionid: 1.0
xRegistry-self: https://example.com/endpoints/ep1/messages/msg1
xRegistry-xid: /endpoints/ep1/messages/msg1
xRegistry-epoch: 1
xRegistry-name: Blob Created
xRegistry-isdefault: true
xRegistry-metaurl: https://example.com/endpoints/ep1/messages/msg1/meta
xRegistry-versionsurl: https://example.com/endpoints/ep1/messages/msg1/versions
xRegistry-versionscount: 1
Location: https://example.com/endpoints/ep1/messages/msg1
Content-Location: https://example.com/endpoints/ep1/messages/msg1/versions/1.0
Content-Disposition: msg1
{
# Definition of a "Blob Created" event (document) excluded for brevity
}
Updates the default Version of a Resource as xRegistry metadata:
PUT /endpoints/ep1/messages/msg1$details
Content-Type: application/json; charset=utf-8
{
"epoch": 1,
"name": "Blob Created",
"description": "a cool event",
"message": {
# Updated definition of a "Blob Created" event excluded for brevity
}
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Location: https://example.com/endpoints/ep1/messages/msg1/versions/1.0
{
"messageid": "msg1",
"versionid": "1.0",
"self": "https://example.com/endpoints/ep1/messages/msg1",
"xid": "/endpoints/ep1/messages/msg1",
"epoch": 2,
"name": "Blob Created",
"isdefault": true,
"description": "a cool event",
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"ancestor": "1.0",
"message": {
# Updated definition of a "Blob Created" event excluded for brevity
},
"metaurl": "https://example.com/endpoints/ep1/messages/msg1/meta",
"versionsurl": "https://example.com/endpoints/ep1/messages/msg1/versions",
"versionscount": 1
}
Update several Versions (adding a label):
PATCH /endpoints/ep1/messages/msg1/versions
Content-Type: application/json; charset=utf-8
{
"1.0": {
"labels": { "customer": "abc" },
},
"2.0": {
"labels": { "customer": "abc" },
},
"3.0": {
"labels": { "customer": "abc" },
}
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"1.0": {
"messageid": "msg1",
"versionid": "1.0",
"labels": { "customer": "abc" },
# Remainder of xRegistry metadata excluded for brevity
},
"2.0": {
"messageid": "msg1",
"versionid": "2.0",
"labels": { "customer": "abc" },
# Remainder of xRegistry metadata excluded for brevity
},
"3.0": {
"messageid": "msg1",
"versionid": "3.0",
"labels": { "customer": "abc" },
# Remainder of xRegistry metadata excluded for brevity
}
]
Note that in this case, the new "label" replaces all existing labels, it is not a "merge" operation because all attributes need to be specified in their entirety.
To retrieve a Resource, an HTTP GET
MAY be used.
The request MUST be of the form:
GET /GROUPS/gID/RESOURCES/rID
This MUST retrieve the default Version of a Resource. Note that rID
will be
the SINGULARid
of the Resource and not the versionid
of the underlying
Version (see Resources).
A successful response MUST either be:
200 OK
with the Resource document in the HTTP body.303 See Other
with the location of the Resource's document being
returned in the HTTP Location
header if the Resource has a RESOURCEurl
value, and the HTTP body MUST be empty.In both cases the Resource's default Version attributes, along with the
meta
and versions
related scalar attributes, MUST be serialized as HTTP
xRegistry-
headers when the Resource's hasdocument
model attribute has a
value of true
.
Note that if the Resource's hasdocument
model attribute has a value of
false
then the "Resource document" will be the xRegistry metadata for the
default Version - same as in the Retrieving a Resource as
Metadata section but without the explicit
usage of $details
.
When hasdocument
is true
, the response MUST be of the form:
HTTP/1.1 200 OK|303 See Other
Content-Type: STRING ?
xRegistry-RESOURCEid: STRING
xRegistry-versionid: STRING
xRegistry-self: URL
xRegistry-xid: URI
xRegistry-epoch: UINT
xRegistry-name: STRING ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
xRegistry-ancestor: STRING
xRegistry-RESOURCEurl: URL ? # If Resource is not in body
xRegistry-metaurl: URL
xRegistry-versionsurl: URL
xRegistry-versionscount: UINTEGER
Location: URL ? # If Resource is not in body
Content-Location: URL ?
Content-Disposition: STRING
... Resource document ... # If RESOURCEurl is not set
Where:
RESOURCEid
MUST be the SINGULARid
of the Resource, not of the default
Version of the Resource.self
MUST be a URL to the Resource, not to the default Version of the
Resource.shortself
, if present, MUST be an alternative URL for self
.xid
MUST be a relative URI to the Resource, not to the default Version of
the Resource.RESOURCEurl
is present then it MUST have the same value as Location
.Content-Location
is present then it MUST be a URL to the Version of the
Resource in the versions
collection - same as defaultversionurl
.Content-Disposition
SHOULD be present and if so, MUST be the RESOURCEid
value. This allows for HTTP tooling that is not aware of xRegistry to know
the desired filename to use if the HTTP body were to be written to a file.When a Resource has the hasdocument
model attribute set to true
, to
retrieve a Resource's metadata (Resource attributes) as a JSON object, an
HTTP GET
with $details
appended to its URL path MAY be used.
The request MUST be of the form:
GET /GROUPS/gID/RESOURCES/rID[$details]
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Location: URL ?
{
"RESOURCEid": "STRING",
"versionid": "STRING",
"self": "URL", # URL to Resource, not default Ver
"shortself": "URL", ?
"xid": "XID", # Relative URI to Resource
"epoch": UINTEGER, # Start of Default Ver attribs
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"ancestor": "STRING",
"contenttype": "STRING", ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If inlined & JSON
"RESOURCEbase64": "STRING", ? # If inlined & ~JSON
"metaurl": "URL",
"meta": {
"RESOURCEid": "STRING", ?
"self": "URL", # URL to "meta" sub-object
"shortself": "URL", ?
"xid": "XID", # Relative URI to "meta" sub-object
"xref": "URL", ?
"epoch": UINTEGER,
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"readonly": BOOLEAN,
"compatibility": "STRING",
"compatibilityauthority": "STRING", ?
"deprecated": { ... }, ?
"defaultversionid": "STRING",
"defaultversionurl": "URL",
"defaultversionsticky": BOOLEAN
},
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { Versions collection } ? # If inlined
}
Where:
$details
suffix is REQUIRED when the Resource's
hasdocument
aspect is true
.RESOURCEid
MUST be the Resource's SINGULARid
, not the versionid
of
the default Version.self
is a URL to the Resource (with $details
suffix if hasdocument
is true
), not to the default Version of the Resource.shortself
, if present, MUST be an alternative URL for self
.xid
is a relative URI to the Resource (without $details
), not to the
default Version of the Resource.RESOURCE
, or RESOURCEbase64
, MUST only be included if requested via use
of the ?inline
feature and RESOURCEurl
is not set.Content-Location
is present then it MUST be a URL to the Version of the
Resource in the versions
collection - same as defaultversionurl
.Examples:
Retrieve a message
Resource as xRegistry metadata:
GET /endpoints/ep1/messages/msg1$details
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Location: https://example.com/endpoints/ep1/messages/msg1/versions/1.0
{
"messageid": "msg1",
"versionid": "1.0",
"self": "https://example.com/endpoints/ep1/messages/msg1","
"xid": "/endpoints/ep1/messages/msg1",
"epoch": 1,
"name": "Blob Created",
"isdefault": true,
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"ancestor": "1.0",
"metaurl": "https://example.com/endpoints/ep1/messages/msg1/meta",
"versionsurl": "https://example.com/endpoints/ep1/messages/msg1/versions",
"versionscount": 1
}
To delete one or more Resources, and all of their Versions, an HTTP DELETE
MAY be used:
DELETE /GROUPS/gID/RESOURCES/rID[?epoch=UINTEGER]
DELETE /GROUPS/gID/RESOURCES
The processing of these two APIs is defined in the Deleting Entities in a Registry Collection section.
Deleting a Resource MUST delete all Versions within the Resource.
Versions represent historical instances of a Resource. When a Resource is
updated, there are two actions that might take place. First, the update can
completely replace an existing Version of the Resource. This is most typically
done when the previous state of the Resource is no longer needed, and there
is no reason to allow people to reference it. The second situation is when
both the old and new Versions of a Resource are meaningful and both might need
to be referenced. In this case, the update will cause a new Version of the
Resource to be created and will have a unique versionid
within the scope
of the owning Resource.
For example, updating the data of Resource without creating a new Version
would make sense if there is a typo in the description
field. But, adding
additional data to the document of a Resource might require a new Version and
a new versionid
(e.g. changing it from "1.0" to "1.1").
This specification does not mandate a particular versioning algorithm or
Version identification (versionid
) scheme.
When serialized as a JSON object, the Version entity adheres to this form:
{
"RESOURCEid": "STRING", # SINGULARid of Resource
"versionid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"isdefault": BOOLEAN,
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"ancestor": "STRING",
"contenttype": "STRING", ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If inlined & JSON
"RESOURCEbase64": "STRING" ? # If inlined & ~JSON
}
Version extension attributes would also appear as additional top-level JSON attributes.
Versions include the following common attributes:
RESOURCEid
- REQUIRED in API and document
views. OPTIONAL in requests.RESOURCEid
of the owning Resource.versionid
- REQUIRED in API and document views.
OPTIONAL in requests.self
- REQUIRED in API and document views.
OPTIONAL in requests. MUST be a URL to this Version, not the owning Resource.shortself
- OPTIONAL in API and document view,
based on the shortself
capability. OPTIONAL in requests.xid
- REQUIRED in API and document views. OPTIONAL
in requests. MUST be the xid
of this Version, not the owning Resource.epoch
- REQUIRED in API and document views. OPTIONAL
in requests. MUST be the epoch
value of this Version, not the owning
Resource.name
- OPTIONAL.description
- OPTIONAL.documentation
- OPTIONAL.labels
- OPTIONAL.createdat
- REQUIRED in API and document views.
OPTIONAL in requests.modifiedat
- REQUIRED in API and document views.
OPTIONAL in requests.ancestor
- REQUIRED in API and document views.
OPTIONAL in requests.contenttype
- OPTIONAL.and the following Version level attributes:
isdefault
- REQUIRED in API and document views
when true
, OPTIONAL when false
. OPTIONAL in requests.RESOURCEurl
- OPTIONAL.RESOURCE
- OPTIONAL.RESOURCEbase64
- OPTIONAL.as defined below:
versionid
AttributeType: String
Description: An immutable unique identifier of the Version.
Constraints:
null
or request
due to these being reserved
for use by the ?setdefaultversionid
feature.Examples:
1.0
v2
v3-rc
isdefault
AttributeType: Boolean
Description: indicates whether this Version is the "default" Version of the
owning Resource. This value is different from other attributes in that it
might often be a calculated value rather than persisted in a datastore.
Thus, when its value changes due to the default Version of a Resource
changing, the Version itself does not change - meaning attributes such as
modifiedat
remains unchanged.
See Creating or Updating Resources and Versions for additional information about this attribute.
Constraints:
false
.true
or false
, case-sensitive.Examples:
true
false
ancestor
AttributeType: String
Description: The versionid
of this Version's ancestor.
The ancestor
attribute MUST be set to the versionid
of this Version's
ancestor If this Version is a root of an ancestor hierarchy tree then it
MUST be set to its own versionid
value.
When creating a Version without explicitly setting the ancestor
attribute, the server MUST set the ancestor
to the most recent Version's
versionid
attribute. If no Versions exist, the ancestor
attribute MUST
be set to the versionid
of the Version being created, making it a root.
If there are existing Versions, the strategy for finding the most recent
Version is as follows:
ancestor
in other
Versions.createdat
date.versionid
attribute.If a write operation contains multiple Versions with the ancestor
attribute
omitted, or set to a value of null
, the server MUST order all of those
Versions based on the createdat
attribute and then alphabetically
(ascending) based on the versionid
. The first Version will have the most
recent Version's versionid
as its ancestor
as clarified above.
When deleting a Version, the server MUST update the ancestor
attribute
of any Version that points to the deleted Version to point to itself,
making it a new root.
If a create operation asks the server to choose the versionid
when
creating a root Version, the versionid
is not yet known and therefore
cannot be assigned the value in the ancestor
attribute. In those cases a
value of request
MUST be used as a way to reference the Version being
processed in the current request.
Constraints:
ancestor
attribute MUST NOT be set to a value that
creates circular references between Versions and it is STRONGLY RECOMMENDED
that the server generate an error
(ancestor_circular_reference) if a request
attempts to do so. For example, an operation that makes Version A's
ancestor B, and Version B's ancestor A, would generate an error.ancestor
attribute to a non-existing versionid
MUST generate an error (invalid_data).ancestor
attribute MUST result in
the owning Version's epoch
and modifiedat
attributes be updated
appropriately.contenttype
AttributeType: String
Description: The media type of the entity as defined by RFC9110.
Constraints:
Content-Type
not
xRegistry-contenttype
like other xRegistry headers.Content-Type
HTTP header.RESOURCEurl
attribute. While this specification cannot guarantee that
this attribute's value will match the Content-Type
returned by an
HTTP GET
to the RESOURCEurl
, it is expected that they will match.Examples:
application/json
RESOURCEurl
AttributeType: URI
Description: if the Resources document is stored outside of the current Registry, then this attribute MUST contain a URL to the location where it can be retrieved. If the value of this attribute is a well-known identifier that is readily understood by registry users and resolves to a common representation of the Resource, or an item in some private store/cache, rather than a networked document location, then it is RECOMMENDED for the value to be a uniform resource name (URN).
Constraints:
GET
to retrieve the contents.hasdocument
model attribute is
set to false
.RESOURCE
AttributeType: Resource Document
Description: This attribute is a serialization of the corresponding Resource document's contents. If the document bytes "as is" allow for them to appear as the value of this JSON attribute, then this attribute MUST be used if the request asked for the document to be inlined in the response.
Constraints
RESOURCE
or RESOURCEbase64
MUST be present.RESOURCEbase64
is also present.hasdocument
model attribute is
set to `false.RESOURCEbase64
AttributeType: String
Description: This attribute is a base64 serialization of the corresponding
Resource document's contents. If the Resource document (which is stored as
an array of bytes) is not conformant with the format being used to serialize
with the Resource object (i.e. as a JSON value), then this attribute MUST be
used in instead of the RESOURCE
attribute.
Constraints:
RESOURCE
or RESOURCEbase64
MUST be present.RESOURCE
is also present.hasdocument
model attribute is
set to `false.If a server does not support client-side specification of the versionid
of a
new Version (see the setversionid
attribute in the Registry
Model), or if a client chooses to not specify the versionid
,
then the server MUST assign new Version an versionid
that is unique within
the scope of its owning Resource.
Servers MAY have their own algorithm for the creation of new Version
versionid
values, but the default algorithm is as follows:
versionid
MUST be a string serialization of a monotonically increasing
(by 1
) unsigned integer starting with 1
and is scoped to the owning
Resource.versionid
is needed, if an existing Version already has
that versionid
then the server MUST generate the next versionid
value
and try again.1
each time, it MUST
continue from the highest previously generated value.With this default versioning algorithm, when semantic versioning is needed,
it is RECOMMENDED to include a major version identifier in the Resource
RESOURCEid
, like "com.example.event.v1"
or "com.example.event.2024-02"
,
so that incompatible, but historically related Resources can be more easily
identified by users and developers. The Version's versionid
then functions
as the semantic minor version identifier.
As Versions of a Resource are added or removed, there needs to be a mechanism by which the "default" one is determined. There are two options for how this might be done:
Newest = Default. The newest Version created (based on createdat
timestamp)
is always the "default" Version. This is the default choice. If more than
one Version has the same "newest" createdat
timestamp, then the choice
the Version with the highest alphabetical versionid
value using
case-insensitive compares MUST be chosen. For example, if there are 3
Versions with versionid
values of v10
, z1
and V2
, then ordering
of the Versions from oldest to newest would be: v10
, V2
, z1
.
Client explicitly chooses the "default". In this option, a client has
explicitly chosen which Version is the "default" and it will not change
until a client chooses another Version, or that Version is deleted (in
which case the server MUST revert back to option 1 (newest = default), if
the client did not use ?setdefaultversionid
to choose the next "default"
Version - see below). This is referred to as the default Version being
"sticky" as it will not change until explicitly requested by a client.
If supported (as determined by the setdefaultversionsticky
model aspect),
a client MAY choose the "default" Version two ways:
defaultversionsticky
and defaultversionid
attributes
in its meta
sub-object. See Resource Attributes
for more information about these attributes.?setdefaultversionid
query parameter that is available on certain
APIs, as defined below.The ?setdefaultversionid
query parameter is defined as:
...?setdefaultversionid=vID
Where:
vID
is the versionid
of the Version that is to become the "default"
Version of the referenced Resource. A value of null
indicates that the
client wishes to switch to the "newest = default" algorithm, in other words,
the "sticky" aspect of the current default Version will be removed. It is
STRONGLY RECOMMENDED that clients provide an explicit value when possible.
However, if a Version create operation asks the server to choose the vID
,
then including that value in the query parameter is not possible. In those
cases a value of request
MAY be used as a way to reference the Version
being processed in the current request, and if the request creates more than
one Version, then an error (too_many_versions) MUST be
generated.null
and non-request
vID
does not reference an existing
Version of the Resource, after all Version processing is completed, then an
error (unknown_id) MUST be generated.Any use of this query parameter on a Resource that has the
setdefaultversionsticky
aspect set to false
MUST generate an error
(bad_flag).
Updating a Resource's defaultversionid
, regardless of the mechanism used to
do so, MUST adhere to the following rules:
null
and request
, its value MUST be
the versionid
of a Version for the specified Resource after all Version
processing is completed (i.e. after any Versions are added or removed). Its
value is not limited to the Versions involved in the current operation.modifiedat
remain unchanged.To retrieve all Versions of a Resource, an HTTP GET
MAY be used.
The request MUST be of the form:
GET /GROUPS/gID/RESOURCES/rID/versions
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <URL>;rel=next;count=UINTEGER ?
{
"KEY": { # The versionid
"RESOURCEid": "STRING", # ID of Resource
"versionid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"isdefault": BOOLEAN,
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"ancestor": "STRING",
"contenttype": "STRING", ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If inlined & JSON
"RESOURCEbase64": "STRING" ? # If inlined & ~JSON
} *
}
Where:
versionid
of the respective
Version.Examples:
Retrieve all Version of a message
Resource:
GET /endpoints/ep1/messages/msg1/versions
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Link: <https://example.com/endpoints/ep1/messages/msg1/versions&page=2>;rel=next;count=100
{
"1.0": {
"messageid": "msg1",
"versionid": "1.0",
"self": "https://example.com/endpoints/ep1/messages/msg1",
"xid": "/endpoints/ep1/messages/msg1",
"epoch": 1,
"name": "Blob Created",
"isdefault": true,
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"ancestor": "1.0"
}
}
See Creating or Updating Resources and Versions.
To retrieve a particular Version of a Resource, an HTTP GET
MAY be used.
The request MUST be of the form:
GET /GROUPS/gID/RESOURCES/rID/versions/vID
A successful response MUST either return the Version or an HTTP redirect to
the RESOURCEurl
value if set.
In the case of returning the Version's document, the response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: STRING ?
xRegistry-RESOURCEid: STRING
xRegistry-versionid: STRING
xRegistry-self: URL
xRegistry-xid: URI
xRegistry-epoch: UINT
xRegistry-name: STRING ?
xRegistry-isdefault: BOOLEAN ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
xRegistry-ancestor: STRING
Content-Disposition: STRING
... Version document ...
Where:
RESOURCEid
MUST be the SINGULARid
of the owning Resource.versionid
MUST be the SINGULARid
of the Version.self
MUST be a URL to the Version, not to the owning Resource.xid
MUST be a relative URI to the Version, not to the owning Resource.Content-Disposition
SHOULD be present and if so, MUST be the RESOURCEid
value. This allows for HTTP tooling that is not aware of xRegistry to know
the desired filename to use if the HTTP body were to be written to a file.In the case of a redirect, the response MUST be of the form:
HTTP/1.1 303 See Other
Content-Type: STRING ?
xRegistry-RESOURCEid: STRING
xRegistry-versionid: STRING
xRegistry-self: URL
xRegistry-xid: URI
xRegistry-epoch: UINT
xRegistry-name: STRING ?
xRegistry-isdefault: BOOLEAN ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
xRegistry-ancestor: STRING
xRegistry-RESOURCEurl: URL
Location: URL
Content-Disposition: STRING
Where:
Location
and RESOURCEurl
MUST have the same value.Examples:
Retrieve a specific Version (1.0
) of a message
Resource:
GET /endpoints/ep1/messages/msg1/versions/1.0
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
xRegistry-messageid: msg1
xRegistry-versionid: 1.0
xRegistry-self: https://example.com/endpoints/ep1/messages/msg1/versions/1.0
xRegistry-xid: /endpoints/ep1/messages/msg1/versions/1.0
xRegistry-epoch: 2
xRegistry-name: Blob Created
xRegistry-isdefault: true
xRegistry-createdat: TIMESTAMP
xRegistry-modifiedat: TIMESTAMP
xRegistry-ancestor: 1.0
Content-Disposition: msg1
{
# Definition of a "Blob Created" event excluded for brevity
}
To retrieve a particular Version's metadata, an HTTP GET
with $details
appended to its RESOURCEid
MAY be used. Note that in cases where the
Resource's hasdocument
is false
then the $details
suffix is OPTIONAL.
The request MUST be of the form:
GET /GROUPS/gID/RESOURCES/rID/versions/vID[$details]
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"RESOURCEid": "STRING",
"versionid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"epoch": UINTEGER,
"name": "STRING", ?
"isdefault": BOOLEAN,
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"ancestor": "STRING",
"contenttype": "STRING", ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If inlined & JSON
"RESOURCEbase64": "STRING" ? # If inlined & ~JSON
}
Where:
RESOURCEid
MUST be the SINGULARid
of the owning Resource.versionid
MUST be the SINGULARid
of the Version.self
MUST be a URL to the Version, not to the owning Resource.xid
MUST be a relative URI to the Version, not to the owning Resource.Examples:
Retrieve a specific Version of a message
Resource as xRegistry metadata:
GET /endpoints/ep1/messages/msg1/versions/1.0$details
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"messageid": "msg1",
"versionid": "1.0",
"self": "https://example.com/endpoints/ep1/messages/msg1/versions/1.0",
"xid": "/endpoints/ep1/messages/msg1/versions/1.0",
"epoch": 2,
"name": "Blob Created",
"isdefault": true,
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"ancestor": "1.0"
}
To delete one or more Versions of a Resource, an HTTP DELETE
MAY be used:
DELETE /GROUPS/gID/RESOURCES/rID/versions/vid[?epoch=UINTEGER&setdefaultversionid=vID]
DELETE /GROUPS/gID/RESOURCES/rID/versions[?setdefaultversionid=vID]
The processing of these two APIs is defined in the Deleting Entities in a
Registry Collection
section. For more information about the ?setdefaultversionid
query
parameter see the Default Version of a
Resource section.
If as a result of one of these operations a Resource has no Versions, then the Resource MUST also be deleted.
A successful response MUST return either:
HTTP/1.1 204 No Content
with an empty HTTP body, or:
HTTP/1.1 200 OK
If, as an extension, the server chooses to return additional data in the HTTP body.
Examples:
Delete a single Version of a message
Resource:
DELETE /endpoints/ep1/messages/msg1/versions/1.0
HTTP/1.1 204 No Content
Any request MAY include a set of query parameters (flags) to control how the response is to be generated. The following sections will define the following flags:
Implementations of this specification SHOULD support all 3 flags.
Note: If the Registry cannot return all expected data in one response because it is too large then it MUST generate an error (too_large). In those cases, the client will need to query the individual inlinable attributes in isolation so the Registry can leverage pagination of the response.
The ?collections
query parameter (flag) MAY be used on requests directed
to the Registry itself or to Group instance to indicate that the response
message MUST NOT include any attributes from the top-level entity (Registry
or Group), but instead MUST include only all of the nested Collection maps
that are defined at that level. Specifying it on a request directed to
some other part of the Registry MUST generate an error (bad_flag).
Use of this flag MUST implicitly turn on inlining - ?inline=*
.
Servers MAY choose include, or exclude, the sibling COLLECTIONSurl
and
COLLECTIONScount
attributes for those top-level collections.
Note that this feature only applies to the root entity of the response and not to any nested entities/collections.
This feature is meant to be used when the Collections of the Registry, or Group, are of interest but not the top-level metadata. For example, this could be used to export one or more Group types from a Registry where the resulting JSON document is then used to import them into another Registry. If the Registry-level attributes were present in the output then they would need to be removed prior to the import, otherwise they would override the target Registry's values.
The resulting JSON, when using this feature, is designed to be used on a
future POST /
operation to either a Registry entity or to a Group instance
as appropriate.
The ?doc
query parameter (flag) MAY be used to indicate that the response
MUST use "document view" when serializing entities and MUST be modified to do
the following:
xref
feature, the target Resource's
attributes are excluded from the source's serialization.self
, COLLECTIONSurl
, metaurl
, defaultversionurl
.All of the relative URLs mentioned in the last bullet MUST begin with #
and be followed by a
JSON Pointer reference to the
entity within the response, e.g. #/endpoints/e1
. This means that they are
relative to the root of the document (response) generated, and not necessarily
relative to the root of the Registry. Additionally, when those URLs are
relative and reference a Resource or Version, the $details
suffix MUST NOT
be present despite the semantics of the suffix being applied (as noted below).
For clarity, if a Registry has a Schema Resource at
/schemagroups/g1/schemas/s1
, then this entity's self
URL (when serialized
in document view) would change based on the path specified on the GET
request:
GET Path | self URL |
---|---|
http://example.com/myreg |
#/schemagroups/g1/schemas/s1 |
http://example.com/myreg/schemagroups |
#/g1/schemas/s1 |
http://example.com/myreg/schemagroups/g1/ |
#/schemas/s1 |
http://example.com/myreg/schemagroups/g1/schemas |
#/s1 |
http://example.com/myreg/schemagroups/g1/schemas/s1 |
#/ |
This feature is useful when a client wants to minimize the amount of data returned by a server because the duplication of that data (typically used for human readability purposes) isn't necessary. For example, if tooling would ignore the duplication, or if the data will be used to populate a new Registry, then this feature might be used. It also makes the output more of a "stand-alone" document that minimizes external references.
For clarity, the serialization of a Resource in document view will adhere to the following:
{
"RESOURCEid": "STRING",
"self": "URL",
"shortself": "URL", ?
"xid": "XID",
"metaurl": "URL",
"meta": {
"RESOURCEid": "STRING",
"self": URL",
"shortself": "URL", ?
"xid": "XID",
"xref": "URL" ?
# The following attributes are absent if 'xref' is set
"epoch": UINTEGER",
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"ancestor": "STRING",
"readonly": BOOLEAN,
"compatibility": "STRING",
"compatibilityauthority": "STRING", ?
"deprecated": { ... }, ?
"defaultversionid": "STRING",
"defaultversionurl": "URL"
"defaultversionsticky": BOOLEAN
}
}
Note that the attributes epoch
through defaultversionsticky
MUST be
excluded if xref
is set because those would be picked-up from the target
Resource's meta
sub-object.
If ?doc
is used on a request directed to a Resource, or Version,
that has the hasdocument
model aspect set to true
, then the processing
of the request MUST take place as if the $details
suffix was specified
in the URL. Meaning, the response MUST be the xRegistry metadata view of the
Resource and not the Resource's "document".
If ?doc
is used on a request directed to a Resource's versions
collection, or to one of its Versions, but the Resource is defined as an
xref
to another Resource, then the server MUST generate an error
(cannot_doc_xref) and SHOULD indicate that using ?doc
on this part of the hierarchy is not valid - due to it not technically existing
in document view.
The ?filter
query parameter (flag) on a request indicates that the response
MUST include only those entities that match the specified filter criteria.
This means that any Registry Collection's attributes MUST be modified
to match the resulting subset. In particular:
url
attribute MUST include the appropriate filter
expression(s) in its query parameters such that an HTTP GET
to that URL
would return the same subset of entities.count
attribute MUST only count the entities that match the
filter expression(s).The format of the ?filter
query parameter is:
filter=EXPRESSION[,EXPRESSION]
Where:
EXPRESSION
values within the scope of one ?filter
query parameter
MUST be evaluated as a logical AND
and any matching entities MUST satisfy
all of the specified expressions within that ?filter
query parameter.?filter
query parameter can appear multiple times and if so MUST
be evaluated as a logical OR
with the other ?filter
query parameters that
appear and the response MUST include all entities that match any of the
individual ?filter
query parameters.The abstract processing logic would be:
?filter
query parameter, find all entities that satisfy all
expressions for that ?filter
. Each will result in a sub-tree of entities.?filter
query parameters, combine those
sub-trees into one result set and remove any duplicates - adjusting any
collection url
and count
values as needed.The format of EXPRESSION
is one of:
[PATH.]ATTRIBUTE
[PATH.]ATTRIBUTE=null
[PATH.]ATTRIBUTE=[VALUE]
[PATH.]ATTRIBUTE!=[VALUE]
Where:
PATH
MUST be a dot (.
) notation traversal of the Registry to the entity
of interest, or absent if at the top of the Registry request. Note that
the PATH
value is based on the requesting URL and not the root of the
Registry. See the examples below. To reference an attribute with a dot as
part of its name, the JSONPath escaping mechanism MUST be used:
['my.name']
. For example, prop1.my.name.prop2
would be specified as
prop1['my.name'].prop2
if my.name
is the name of one attribute.PATH
MUST only consist of valid GROUPS
, RESOURCES
or versions
,
otherwise an error (invalid_data) MUST be generated.ATTRIBUTE
MUST be the attribute in the entity to be examined.labels
) MUST use dot (.
) to reference nested
attributes. For example: labels.stage=dev
for a filter.?filter=myobj.myattr=5
would not match if: myobj
is missing, myattr
is
missing, or myattr
is not 5
.ATTRIBUTE
present with any non-null
value.=
operator:
VALUE
is null
then only entities without the specified
ATTRIBUTE
MUST be included in the response.null
VALUE
is specified then VALUE
MUST be the desired
value of the attribute being examined. Only entities whose specified
ATTRIBUTE
with this VALUE
MUST be included in the response.VALUE
is absent then the implied VALUE
is an empty string and
the matching MUST be done as specified in the previous bullet.!=
operator:
VALUE
is null
then it MUST have the same semantics as
?filter=ATTRIBUTE
as specified above (present with any non-null
value).VALUE
is non-null
then only entities without the specified
ATTRIBUTE
and VALUE
MUST be included in the response. This MUST be
semantically equivalent to NOT(ATTRIBUTE=VALUE)
, and this also means
that if ATTRIBUTE
is missing then that attribute will match the filter.When comparing an ATTRIBUTE
to the specified VALUE
the following rules
MUST apply for an entity to be considered a match of the filter expression:
true
or false
).*
) character appears in the VALUE
. The presence of a wildcard
indicates that any number of characters can appear at that location in the
VALUE
. The wildcard MAY be escaped via the use of a backslash (\\
)
character (e.g. abc\*def
) to mean that the *
is to be interpreted as
a normal character and not as a wildcard. Note that a VALUE
of *
MUST
be equivalent to checking for the existence of the attribute, with any
value (even an empty string). In other words, the filter will only fail if
the attribute has no value at all.If the request references an entity (not a collection), and the EXPRESSION
references an attribute in that entity (i.e. there is no PATH
), then if the
EXPRESSION
does not match the entity, that entity MUST NOT be returned. In
other words, a 404 Not Found
would be generated in the HTTP protocol case.
Examples:
Request PATH | Filter query | Commentary |
---|---|---|
/ | ?filter=endpoints.description=*cool* |
Only endpoints with the word cool in the description |
/endpoints | ?filter=description=*CooL* |
Similar results as previous, with a different request URL |
/ | ?filter=endpoints.messages.versions.versionid=1.0 |
Only versions (and their owning parents) that have a versionid of 1.0 |
/ | ?filter=endpoints.name=myendpoint,endpoints.description=*cool*& filter=schemagroups.labels.stage=dev |
Only endpoints whose name is myendpoint and whose description contains the word cool , as well as any schemagroups with a label name/value pair of stage/dev |
/ | ?filter=description=no-match |
Returns a 404 if the Registry's description doesn't equal no-match |
/ | ?filter=endpoints.messages.meta.readonly=true |
Only messages that are readonly |
Specifying a filter does not imply inlining. However, inlining can be used at the same time but MUST NOT result in additional entities being included in the results unless they are children of a matching leaf entity.
For example, in the following entity URL paths representing a Registry:
mygroups/g1/myresources/r1/versions/v1
mygroups/g1/myresources/r1/versions/v2
mygroups/g1/myresources/r2/versions/v1
mygroups/g2/myresources/r3/versions/v1
This request:
GET /?filter=mygroups.myresources.myresourceid=r1&inline=*
would result in the following entities (and their parents along the specified paths) being returned:
mygroups/g1/myresources/r1/versions/v1 # versions are due to inlining
mygroups/g1/myresources/r1/versions/v2
However, this request:
GET /?filter=mygroups.mygroupid=g2&filter=mygroups.myresources.myresourceid=r1&inline=*
would result in the following returned:
mygroups/g1/myresources/r1/versions/v1 # from 2nd ?filter
mygroups/g1/myresources/r1/versions/v2 # from 2nd ?filter
mygroups/g2/myresources/r3/versions/v1 # from 1nd ?filter
And, this request:
GET /?filter=mygroups.mygroupid=g1&filter=mygroups.myresources.myresourceid=r1&inline=*
would result in the following being returned:
mygroups/g1/myresources/r1/versions/v1 # from 2nd ?filter
mygroups/g1/myresources/r1/versions/v2 # from 2nd ?filter
mygroups/g1/myresources/r2/versions/v1 # from 1st ?filter
And, finally this request:
GET /?filter=mygroups.mygroupid=g1,mygroups.myresources.myresourceid=r1&inline=*
would result in the following being returned:
mygroups/g1/myresources/r1/versions/v1
mygroups/g1/myresources/r1/versions/v2
Notice the first part of the ?filter
expression (to the left of the "and"
(,
)) has no impact on the results because the list of resulting leaves in
that subtree is not changed by that search criteria.
The ?inline
query parameter (flag) MAY be used on requests to indicate whether
nested collections/objects, or certain (potentially large) attributes, are to
be included in the response message.
The ?inline
query parameter on a request indicates that the response
MUST include the contents of all specified inlinable attributes. Inlinable
attributes include:
model
attribute on the Registry entity.capabilities
attribute on the Registry entity.GROUPS
,
RESOURCES
and versions
.RESOURCE
attribute in a Resource or Version.meta
attribute in a Resource.While the RESOURCE
and RESOURCEbase64
attributes are defined as two
separate attributes, they are technically two separate "views" of the same
underlying data. As such, the usage of each will be based on the content type
of the Resource, specifying RESOURCE
in the ?inline
query parameter MUST
be interpreted as a request for the appropriate attribute. In other words,
RESOURCEbase64
is not a valid inlinable attribute name.
Use of this feature is useful for cases where the contents of the Registry are to be represented as a single (self-contained) document.
Some examples:
GET /?inline=model
# Just 'model'GET /?inline=model,endpoints
# Model and one level under endpoints
GET /?inline=*
# Everything except 'model'GET /?inline=model,*
# Everything, including 'model'GET /?inline=endpoints.messages
# One level below 'endpoints.messages'GET /?inline=endpoints.*
# Everything below 'endpoints'GET /endpoints/ep1/?inline=messages.message
# Just 'message'GET /endpoints/ep1/messages/msg1?inline=message
# Just 'message'The format of the ?inline
query parameter is:
?inline[=PATH[,...]]
Where PATH
is a string indicating which inlinable attributes to show in
the response. References to nested attributes are represented using a
dot (.
) notation where the xRegistry collections names along the hierarchy
are concatenated. For example: endpoints.messages.versions
will inline all
Versions of Messages. Non-leaf parts of the PATH
MUST only reference
xRegistry collection names and not any specific entity IDs since PATH
is
meant to be an abstract traversal of the model.
To reference an attribute with a dot as part of its name, the JSONPath
escaping mechanism MUST be used: ['my.name']
. For example,
prop1.my.name.prop2
would be specified as prop1['my.name'].prop2
if
my.name
is the name of an attribute.
There MAY be multiple PATH
s specified, either as comma separated values on
a single ?inline
query parameter or via multiple ?inline
query parameters.
The *
value MAY be used to indicate that all nested inlinable attributes
at that level in the hierarchy (and below) MUST be inlined - except model
and capabilities
at the root of the Registry. These two are excluded since
the data associated with them are configuration related. To include their data
the request MUST include PATH
values of model
or capabilities
. Use of
*
MUST only be used as the last part of the PATH
(in its entirety). For
example, foo*
and *.foo
are not valid PATH
values, but *
and
endpoints.*
are.
An ?inline
query parameter without any value MAY be supported and if so it
MUST have the same semantic meaning as ?inline=*
.
The specific value of PATH
will vary based on where the request is directed.
For example, a request to the root of the Registry MUST start with a GROUPS
name, while a request directed at a Group would start with a RESOURCES
name.
For example, given a Registry with a model that has endpoints
as a Group and
messages
as a Resource within endpoints
, the table below shows some
PATH
values and a description of the result:
HTTP GET Path |
Example ?inline=PATH values | Comment |
---|---|---|
/ | ?inline=endpoints | Inlines the endpoints collection, but just one level of it, not any nested inlinable attributes |
/ | ?inline=endpoints.messages.versions | Inlines the versions collection of all messages. Note that this implicitly means the parent attributes (messages and endpoints would also be inlined - however any other GROUPS or RESOURCE s types would not be |
/endpoints | ?inline=messages | Inlines just messages and not any nested attributes. Note we don't need to specify the parent GROUP since the URL already included it |
/endpoints/ep1 | ?inline=messages.versions | Similar to the previous endpoints.messages.version example |
/endpoints/ep1 | ?inline=messages.message | Inline the Resource itself |
/endpoints/ep1 | ?inline=endpoints | Invalid, already in endpoints and there is no RESOURCE called endpoints |
/ | ?inline=endpoints.messages.meta | Inlines the meta attributes/sub-object of each message returned. |
/ | ?inline=endpoints.* | Inlines everything for all endpoints . |
Note that asking for an attribute to be inlined will implicitly cause all of its parents to be inlined as well, but just the parent's collections needed to show the child. In other words, just the collection in the parent in which the child appears, not all collections in the parent.
When specifying a collection to be inlined, it MUST be specified using the plural name for the collection in its defined case.
A request to inline an unknown, or non-inlinable, attribute MUST generate an error (invalid_data).
Some attributes can contain arbitrary UTF-8 string content, and per RFC7230, section 3, HTTP headers MUST only use printable characters from the US-ASCII character set, and are terminated by a CRLF sequence with OPTIONAL whitespace around the header value.
When encoding an attribute's value as an HTTP header, it MUST be percent-encoded as described below. This is compatible with RFC3986, section 2.1 but is more specific about what needs encoding. The resulting string SHOULD NOT be further encoded. (Rationale: quoted string escaping is unnecessary when every space and double-quote character is already percent-encoded.)
When decoding an HTTP header into an attribute's value, any HTTP header value MUST first be unescaped with respect to double-quoted strings, as described in RFC7230, section 3.2.6. A single round of percent-decoding MUST then be performed as described below. HTTP headers for attribute values do not support parenthetical comments, so the initial unescaping only needs to handle double-quoted values, including processing backslash escapes within double-quoted values. Header values produced via the percent-encoding described here will never include double-quoted values, but they MUST be supported when receiving events, for compatibility with older versions of this specification which did not require double-quote and space characters to be percent-encoded.
Percent encoding is performed by considering each Unicode character within the attribute's canonical string representation. Any character represented in memory as a [Unicode surrogate pair][surrogate-pair] MUST be treated as a single Unicode character. The following characters MUST be percent-encoded:
Space and double-quote are encoded to avoid requiring any further quoting. Percent is encoded to avoid ambiguity with percent-encoding itself.
Steps to encode a Unicode character:
%xy
where x
is a
hexadecimal representation of the most significant 4 bits of the byte,
and y
is a hexadecimal representation of the least significant 4
bits of the byte.Percent-encoding SHOULD be performed using upper-case for values A-F, but decoding MUST accept lowercase values.
When performing percent-decoding, values that have been unnecessarily percent-encoded MUST be accepted, but encoded byte sequences which are invalid in UTF-8 MUST generate an error (header_decoding_error). For example, "%C0%A0" is an overlong encoding of U+0020, and would be rejected.
Example: a header value of "Euro € 😀" SHOULD be encoded as follows:
If an error occurs during the processing of a request, even if the error was
during the creation of the response (e.g. an invalid ?inline
value was
provided), then an error MUST be generated and the entire request MUST be
undone.
In general, when an error is generated, it SHOULD be sent back to the client. However, this MAY not happen if the server determines there is a good reason to not do so - such as due to security concerns.
Most of the error conditions mentioned in this specification will include a reference to one of the errors listed in this section. While it is RECOMMENDED that implementations use those errors (for consistency), they MAY choose to use a more appropriate one (or a custom one).
When an error is transmitted back to clients, it SHOULD adhere to the format specified in this section - which references the Problem Details for HTTP APIs specification, and when used MUST be of the following form:
HTTP/1.1 CODE
Content-Type: application/json; charset=utf-8
{
"type": "URI",
"instance": "URL",
"title": "STRING",
"detail": "STRING", ?
... error specific fields ...
}
Where:
CODE
is the HTTP response code and status text (e.g. 404 Not Found
).CODE
, "type"
, "instance"
and title
fields are REQUIRED. All other
fields are OPTIONAL unless overwise stated as part of the error definition. Any
substitutable information defined as part of an error MUST be populated
appropriately.
HTTP response codes and status text are defined in the HTTP Semantics specification.
In the following list of errors, the Code
, Type
and Instance
values MUST
be as specified. The other field values are recommendations and MAY be modified
as appropriate, including being specified in a language other than English.
https://github.com/xregistry/spec/blob/main/core/spec.md#ancestor_circular_reference
400 Bad Request
The assigned "ancestor" value ({ancestor_value}) creates a circular reference
https://github.com/xregistry/spec/blob/main/core/spec.md#api_not_found
404 Not Found
The specified path ({invalid path}) is not supported
https://github.com/xregistry/spec/blob/main/core/spec.md#bad_flag
400 Bad Request
The specified query parameter ({query parameter}) is not allowed in this context
This error is purposely generic and can be used when there isn't a more condition-specific error that would be more appropriate. Implementations SHOULD attempt to use a more specific error when possible.
https://github.com/xregistry/spec/blob/main/core/spec.md#bad_request
400 Bad Request
The request can not be processed as provided
https://github.com/xregistry/spec/blob/main/core/spec.md#cannot_doc_xref
400 Bad Request
Retrieving the document view of an xref'd Resource's Versions is not possible
https://github.com/xregistry/spec/blob/main/core/spec.md#capability_error
400 Bad Request
There was an error in the capabilities provided
https://github.com/xregistry/spec/blob/main/core/spec.md#compatibility_violation
400 Bad Request
The request would cause one or more Versions of this Resource to violate the Resource's compatibility rules ({compatibility attribute value})
https://github.com/xregistry/spec/blob/main/core/spec.md#data_retrieval_error
500 Internal Server Error
The server was unable to retrieve all of the requested data
https://github.com/xregistry/spec/blob/main/core/spec.md#details_required
400 Bad Request
$details suffixed is needed when using PATCH for this Resource
https://github.com/xregistry/spec/blob/main/core/spec.md#extra_xregistry_headers
400 Bad Request
xRegistry HTTP headers are not allowed on this request
https://github.com/xregistry/spec/blob/main/core/spec.md#header_decoding_error
400 Bad Request
The value ("{header value}") of the HTTP "{header name}" header can not be decoded
https://github.com/xregistry/spec/blob/main/core/spec.md#invalid_character
400 Bad Request
An invalid character ({the character}) was specified an attribute's name ({full attribute name})
https://github.com/xregistry/spec/blob/main/core/spec.md#invalid_data
400 Bad Request
The data provided for "{attribute/parameter name}" is invalid
https://github.com/xregistry/spec/blob/main/core/spec.md#invalid_data_type
405 Bad Request
A value of an incorrect data-type was specified
https://github.com/xregistry/spec/blob/main/core/spec.md#method_not_allowed
405 Method Not Allowed
The specified HTTP method ({invalid method}) is not supported for: {request URL}
https://github.com/xregistry/spec/blob/main/core/spec.md#mismatched_epoch
400 Bad Request
The specified epoch value ({invalid epoch}) does not match its current value ({current epoch})
https://github.com/xregistry/spec/blob/main/core/spec.md#mismatched_id
400 Bad Request
The specified {singular type name} ID value ({invalid id}) needs to be "{expected id}"
https://github.com/xregistry/spec/blob/main/core/spec.md#misplaced_epoch
400 Bad Request
The specified "epoch" value needs to be within a "meta" sub-object
https://github.com/xregistry/spec/blob/main/core/spec.md#missing_versions
400 Bad Request
At least one Version needs to be included in the request
https://github.com/xregistry/spec/blob/main/core/spec.md#model_compliance_error
400 Bad Request
The model provided would cause one or more entities in the Registry to become non-compliant
https://github.com/xregistry/spec/blob/main/core/spec.md#model_error
400 Bad Request
There was an error in the model definition provided
https://github.com/xregistry/spec/blob/main/core/spec.md#multiple_roots
400 Bad Request
The operation would result in multiple root Versions which is not allowed by this Registry
https://github.com/xregistry/spec/blob/main/core/spec.md#not_found
404 Not Found
The specified entity can not be found
https://github.com/xregistry/spec/blob/main/core/spec.md#readonly
400 Bad Request
Updating a read-only entity ({XID of entity}) is not allowed
https://github.com/xregistry/spec/blob/main/core/spec.md#required_attribute_missing
400 Bad Request
One or more mandatory attributes are missing
This error MAY be used when it appears that the incoming request was valid but something unexpected happened in the server that caused an error condition.
https://github.com/xregistry/spec/blob/main/core/spec.md#server_error
500 Internal Server Error
An unexpected error occurred, please try again later
https://github.com/xregistry/spec/blob/main/core/spec.md#too_large
406 Not Acceptable
The size of the response is too large to return in a single response
https://github.com/xregistry/spec/blob/main/core/spec.md#too_many_versions
400 Bad Request
The request is only allowed to have one Version specified
https://github.com/xregistry/spec/blob/main/core/spec.md#unknown_attribute
400 Bad Request
An unknown attribute ({attribute name}) was specified
https://github.com/xregistry/spec/blob/main/core/spec.md#unknown_id
400 Bad Request
The "{singular name of the entity type}" with the ID "{the unknown ID}" can not be found
https://github.com/xregistry/spec/blob/main/core/spec.md#unsupported_specversion
400 Bad Request
The specified "specversion" value is not supported