A Registry Service exposes Resources, and their metadata, for the purposes of enabling discovery of those Resources for either end-user consumption or automation and tooling.
- Overview
- Notations and Terminology
- Registry Attributes and APIs
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.
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 exists under a single Group and, similar to Groups, has 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",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"model": { # Only if inlined/nested
"schemas": [ "STRING" * ], ? # Available schema formats
"attributes": { # Registry level attributes/extensions
"STRING": { # Attribute name (case sensitive)
"name": "STRING", # Same as attribute's key
"type": "TYPE", # string, decimal, array, object, ...
"description": "STRING", ?
"enum": [ VALUE * ], ? # Array of scalar 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
"clientrequired": BOOLEAN, ? # Default: false
"serverrequired": BOOLEAN, ? # Default: false
"default": VALUE, ? # Attribute's default value, scalars
"attributes": { ... }, ? # If "type" above is object
"item": { # If "type" above is map,array
"type": "TYPE", ? # map value type, or array type
"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"
"attributes": { ... }, ? # Group level attributes/extensions
"resources": {
"STRING": { # Key=plural name, e.g. "messages"
"plural": "STRING", # e.g. "messages"
"singular": "STRING", # e.g. "message"
"maxversions": UINTEGER, ? # Num Vers(>=0). Default=0, 0=unlimited
"setversionid": BOOLEAN, ? # Supports client specified Version IDs
"setdefaultversionsticky": BOOLEAN, ? # Supports client "default" selection
"hasdocument": BOOLEAN, ? # Has separate document. Default=true
"typemap": MAP, ? # contenttype mappings
"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/nested
"KEY": { # Key=the Group id
"GROUPid": "STRING", # The Group ID
"self": "URL",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"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/nested
"KEY": { # The Resource id
# These are inherited from the "default" Version (except 'self')
"RESOURCEid": "STRING",
"versionid": "STRING",
"self": "URL", # Resource URL, not Version
"epoch": UINTEGER, # Version's epoch
"name": "STRING", ?
"isdefault": true,
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"contenttype": "STRING, ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If local & inlined & JSON
"RESOURCEbase64": "STRING", ? # If local & inlined & ~JSON
# Resource level helper attributes
"metaurl": "URL",
"meta": { # Only if inlined
"RESOURCEid": "STRING",
"self": "URL", # URL to "meta" object
"xref": "URL", ? # Ptr to linked Resource
"epoch": UINTEGER, # Resource's epoch
"createdat": "TIMESTAMP", # Resource's
"modifiedat": "TIMESTAMP", # Resource's
"readonly": BOOLEAN, ? # Default is "false"
"compatibility": "STRING", ? # Default is "none"
# TODO - add timestamp?
"defaultversionid": "STRING",
"defaultversionurl": "URL",
"defaultversionsticky": BOOLEAN ? # Default is "false"
}, ?
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { # Only if inlined/nested
"KEY": { # The Version's versionid
"RESOURCEid": "STRING", # The Resource id
"versionid": "STRING", # The Version id
"self": "URL", # Version URL
"epoch": UINTEGER, # Version's epoch
"name": "STRING", ?
"isdefault": BOOLEAN, ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"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 servers MUST be prepared for them to appear in incoming requests and MUST support them since "support" simply means persisting them in the backing datastore. However, as with all attributes, if accepting the attribute would result 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
form of a Group and Resource type being used. While GROUPS
and RESOURCES
are the plural form of those respective types. Use of the word SINGULAR
represents the singular form of the entity being referenced.
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.
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
URIREFERENCE
URITEMPLATE
URL
TYPE
- one of the allowable data type names (MUST be in lower case) listed in Attributes and Extensions
This 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.
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 question is meant for use as a stand-alone document or as part of a REST API message exchange - if there is a difference between the two usages.
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.- Some serializations, such as JSON, allow for a
null
type of value to appear in 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 the xRegistry sincenull
is not a validinteger
. 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.
- Some serializations, such as JSON, allow for a
boolean
- case sensitivetrue
orfalse
.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.- Each key MUST be a non-empty string consisting of only lowercase
alphanumeric characters (
[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. - See Serializing Resource Documents for more information about serializing maps as HTTP headers.
- Each key MUST be a non-empty string consisting of only lowercase
alphanumeric characters (
object
- a nested entity made up of a set of attributes of these data types.string
- sequence of Unicode characters.timestamp
- an RFC3339 timestamp.uinteger
- unsigned integer.uri
- absolute URI as defined in RFC 3986 Section 4.3.urireference
- URI-reference as defined in RFC 3986 Section 4.1.uritemplate
- URI Template as defined in RFC 6570 Section 3.2.1.url
- URL as defined in RFC 1738.
The "scalar" data types are: boolean
, decimal
, integer
, string
,
timestamp
, uinteger
, uri
, urireference
, uritemplate
, url
.
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:
- Their names MUST be between 1 and 63 characters in length.
- Their names MUST only contain lowercase alphanumeric characters or an
underscore (
[a-z0-9_]
) and MUST NOT start with a digit ([0-9]
). - For STRING attributes, an empty string is a valid value and MUST NOT be treated the same as an attribute with no value (or absence of the attribute).
- For scalar attributes, the string serialization of the attribute name and
its value MUST NOT exceed 4096 bytes. This is to ensure that it can appear
in an HTTP header without exceeding implementation limits (see
RFC6265/Limits).
In cases where larger amounts of data is needed, it is RECOMMENDED that
an attribute (of type URL) be defined that references a separate
document. For example,
documentation
can be considered such an attribute fordescription
. - If an attribute's type is not fully defined (i.e. it is defined as an
any
type) but a concrete type is needed to successfully process it, then the server SHOULD default it to typestring
. For example, if an extension is defined as a map whose values are of typeany
, but it appears in an HTTP header with a value of5
(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, thenstring
is the default choice. - There might be cases when it is not possible to know whether a field name is
part of an object (in which case it is an "attribute name"), or is part of
a map (in which case it is a "key name"). This decision would impact
verification of the field since key names allow for a superset of the
characters allowed for attribute names. This will only happen when the
any
type has been used higher-up in the model. As a result, any portion of the entity that appears under the scope of anany
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.
Implementations of this specification MAY define additional (extension) attributes. However they MUST adhere to the following rules:
- All attributes MUST conform to the model definition of the Registry. This
means that they MUST satisfy at least one of the following:
- Be explicitly defined (by name) as part of the model.
- Be permitted due to the presence of the
*
(undefined) extension attribute name at that level in the model. - Be permitted due to the presence of an
any
type for one of its parent attribute definitions.
- They MUST NOT change the semantics of the Registry - they MUST only be additional metadata to be persisted in the Registry since servers MUST persist all valid extensions. TODO is this really ok?
- They MUST NOT conflict with the name of an attribute defined by this
specification, including the
RESOURCE*
andCOLLECTION*
attributes that are implicitly defined. Note that if a Resource type has thehasdocument
attribute set thefalse
then this rule does not apply for theRESOURCE*
attributes as those attributes are not used for that Resource type. - It is RECOMMENDED that extension attributes on different entities not use the same name unless they have the exact same semantic meaning.
- It is STRONGLY RECOMMENDED that they be named in such a way as to avoid potential conflicts with future Registry level attributes. For example, use of a model (or domain) specific prefix could be used to help avoid possible future conflicts.
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 these attributes adheres to this form:
"SINGULARid": "STRING"
"self": "URL"
"epoch": UINTEGER
"name": "STRING"
"description": "STRING"
"documentation": "URL"
"labels": { "STRING": "STRING" * }
"origin": "URI"
"createdat": "TIMESTAMP"
"modifiedat": "TIMESTAMP"
The definition of each attribute is defined below:
- Type: 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 ofschemaid
. This attribute MUST be namedregistryid
for the Registry itself, and MUST be namedversionid
for all Version entities. - Constraints:
- MUST be a non-empty string consisting of RFC3986
unreserved
characters (ALPHA / DIGIT / "-" / "." / "_" / "~"). - MUST be case insensitive unique within the scope of the entity's parent.
In the case of the
registryid
for the Registry itself, the uniqueness scope will be based on where the Registry is used. For example, a publicly accessible Registry might want to consider using a globally unique value, while a private Registry does not need to be so widely unique. - This attribute MUST be treated as case sensitive for look-up purposes.
This means that an HTTP request to an entity with the wrong case for its
SINGULARid
MUST be treated as "not found". - In cases where an entity's
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 theSINGULARid
outside of the entity, and it MUST be the same as the entity's existingSINGULARid
if one exists, otherwise an HTTP400 Bad Request
error MUST be generated. - MUST be immutable.
- MUST be a non-empty string consisting of RFC3986
- 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 can be deleted.
-
Type: URL
-
Description: A unique absolute URL for an entity. In the case of pointing to an entity in a Registry Collection, the URL MUST be a combination of the base URL for the collection appended with the
SINGULARid
of the entity.When this URL references a Resource or Version, and the Resource has the
hasdocument
model aspect set totrue
, then it MUST include$structure
appended to itsSINGULARid
if the request asked for the serialization of the xRegistry metadata. This would happen when$structure
was used in the request, or when the Resource (or Version) is included in the serialization of a parent entity.If the Resource or Version has the
hasdocument
model aspect set tofalse
, then this URL MUST be appended with$structure
only if the request was directed at the Resource or Version and included$structure
. Otherwise in all other situations$structure
MUST NOT be included. -
Constraints:
- MUST be a non-empty absolute URL.
- MUST be a read-only attribute in API view.
-
Examples:
https://example.com/registry/endpoints/ep1
-
Type: 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 atouch
type of operation.During a single write operation, whether this value is incremented for each modified attribute of an 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 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 noepoch
attribute at all.The semantics of
epoch
are the same as the semantics of the HTTP ETag and HTTP If-Match Headers, and as such the following rules apply when using HTTP as the protocol:- When the response body references a single entity, its
epoch
value MUST also appear as an HTTPETag
header. Note that a response body with a map or array that only includes one entity does not satisfy this requirement. - When the xRegistry metadata of a Resource, or Version, is serialized as
HTTP headers, and prefixed with
xRegistry-
, thexRegistry-epoch
header MUST NOT be present since theETag
header MUST already be present with the value. - When a request includes an HTTP
If-Match
header, then its value MUST match the existing entity'sepoch
value. And if not, a412 Precondition Failed
error MUST be generated. Note, that*
MUST match all possibleepoch
value, but will fail if the entity does not already exist (per the HTTP specification). - The
If-Match
verification MUST be done in addition to theepoch
checking specified above. In other words, a present but non-matching value in either location MUST generate an error.
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, itsepoch
value MUST NOT be updated solely due to modifications of an existing entity in the collection. - When the response body references a single entity, its
-
Constraints:
- MUST be an unsigned integer equal to or greater than zero.
- MUST increase in value each time the entity is updated.
-
Examples:
0
,1
,2
,3
-
Type: 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 theSINGULARid
might be something that isn't human friendly, like a UUID. In cases wherename
is OPTIONAL and absent, theSINGULARid
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 SHOULD NOT be used for unique identification purposes, the
SINGULARid
SHOULD be used instead.Note that implementations MAY choose to enforce additional constraints on this value. For example, they could mandate that
SINGULARid
andname
be the same value. Or, it could mandate thatname
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:
- If present, MUST be non-empty.
-
Examples:
My Endpoints
- Type: String
- Description: A human readable summary of the purpose of the entity.
- Constraints:
- None
- Examples:
A queue of the sensor generated messages
- Type: 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:
- If present, MUST be a non-empty URL.
- MUST support an HTTP(s)
GET
to this URL.
- Examples:
https://example.com/docs/myQueue
-
Type: 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:
- If present, MUST be a map of zero or more name/value string pairs. See Attributes and Extensions for more information.
- Keys MUST be non-empty strings.
- Values MAY be empty strings.
-
Examples:
"labels": { "owner": "John", "verified": "" }
when in the HTTP bodyxRegistry-labels-owner: John
xRegistry-labels-verified:
when in HTTP headers
Note: 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 semi-colon(;
) is used instead of colon(:
). So, this might be something to consider when choosing to use labels that can be empty strings.
- Type: URI
- Description: A URI reference to the original source of the entity. This
can be used to locate the true authority owner of the entity in cases of
distributed Registries. If this attribute is absent its default value
is the value of the
self
attribute and in those cases its presence in the serialization of the entity is OPTIONAL. - Constraints:
- OPTIONAL if this Registry is the authority owner.
- REQUIRED if this Registry is not the authority owner.
- If present, MUST be a non-empty URI.
- Examples:
https://example2.com/myregistry/endpoints/ep1
- Type: Timestamp
- Description: The date/time of when the entity was created.
- Constraints:
- MUST be a RFC3339 timestamp.
- If present in a write operation request, the value MUST override any
existing value, however a value of
null
MUST use the current date/time as the new value. - When absent in a write operation request, any existing value MUST remain unchanged, or if not present, set to the current date/time
- In cases where the
createdat
attribute is set to the current date/time on multiple entities within the same operation, the same value MUST be applied to all of the entities.
- Examples:
2030-12-19T06:00:00Z
- Type: Timestamp
- Description: The date/time of when the entity was last updated
- Constraints:
- MUST be a RFC3339 timestamp representing the time when the entity was last updated.
- Any update operation (even one that does not change any attribute, such as
a
PATCH
with no attributes provided), MUST update this attribute. This then acts like atouch
type of operation. - Upon creation of a new entity, this attribute MUST match the
createdat
attribute's value. - Updates to an existing entity in an xRegistry collection MUST NOT cause an
update to its parent entity's
modifiedat
value. However, adding or removing an entity from a nested xRegistry collection MUST update themodifiedat
value of the parent entity. - If present in a write operation request, the following applies:
- If the request value is the same as the existing value, then the current date/time MUST be used as its new value.
- If the request value is different than the existing value, then the request value MUST be used as its new value.
- If the request value is
null
then the current date/time MUST be used as the new value.
- When absent in a write operation request, it MUST be set to the current date/time.
- In cases where the
modifiedat
attribute is set to the current date/time on multiple entities within the same operation, the same value MUST be applied to all of the entities.
- Examples:
2030-12-19T06:00:00Z
This specification defines the following API patterns:
/ # Access the Registry
/model # Access the model definitions
/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
Where:
GROUPS
is a Group type name (plural). e.g.endpoints
.GROUP
, not shown, is the singular name of a Group type.gID
is theSINGULARid
of a single Group.RESOURCES
is a Resource type name (plural). e.g.messages
.RESOURCE
, not shown, is the singular name of a Resource type.rID
is theSINGULARid
of a single Resource.vID
is theversionid
of a single Version of a Resource.
These acronym definitions apply to the remainder of this specification.
While these APIs are shown to be at the root path of a Registry Service, implementation 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 API is not supported by the server then a 405 Method Not Allowed
HTTP response MUST be generated.
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:
- A
PUT
orPOST
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 MUST be reset to their default values rather than deleted. - A
PATCH
operation MUST only modify the attributes explicitly mentioned in the request. Any attribute with a value ofnull
MUST be interpreted as a request to delete the attribute, and as withPUT
/POST
, server managed attributes might have specialized processing. - On write operations, without a
?nested
query parameter, any included inlinable attributes (exceptRESOURCE*
) MUST generate an error. PUT
MUST NOT be targeted at xRegistry collections. APOST
orPATCH
MUST be used instead to add entities to the collection, and aDELETE
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 aPOST
to a Resource URL MUST be treated as an alias for aPOST
to the Resource'sversions
collection.- Unknown query parameters MUST be silently ignored by servers.
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 anyepoch
attribute included in the request MUST be ignored.?nodefaultversionid
- presence of this query parameter indicates that anydefaultversionid
attribute included in the request MUST be ignored.?nodefaultversionsticky
- presence of this query parameter indicates that anydefaultversionsticky
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.
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 $structure
suffix on the URL path. This can be achieved by
creating a secondary sibling file on disk with $structure
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:
- The term
COLLECTIONS
MUST be the plural name of the collection (e.g.endpoints
,versions
). - The
COLLECTIONSurl
attribute MUST be an absolute URL that can be used to retrieve theCOLLECTIONS
map via an HTTP(s)GET
(including any necessary filter) 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 ({}
). - The
COLLECTIONScount
attribute MUST contain the number of entities in theCOLLECTIONS
map (after any necessary filtering) and MUST be a read-only attribute that MUST be silently ignored by a server during an write operation. - The
COLLECTIONS
attribute is a map and MUST contain the entities of the collection (after any necessary filtering), and MUST use theSINGULARid
of each entity as its map key. - The key of each entity in the collection MUST be unique within the scope of the collection.
- The specifics of whether each attribute is REQUIRED or OPTIONAL will be based whether document or API view is being used - see the next section.
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
andCOLLECTIONScount
are OPTIONAL.COLLECTIONS
is REQUIRED.
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
andCOLLECTIONScount
are OPTIONAL for requests and MUST be silently ignored by the server if present.COLLECTIONS
is OPTIONAL for responses and MUST only be included if the request included the?inline
query parameter indicating that this collection's entities are to be returned. If?inline
is present thenCOLLECTIONS
is REQUIRED and MUST be present in the response even if it is empty (e.g.{}
).COLLECTIONS
is OPTIONAL for requests and MUST generate an error if the?nested
query parameter is not present. 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.
A request that includes COLLECTIONS
attributes MUST generate an error
if the ?nested
query parameter is not present. This acts as a check to
ensure the client is not accidentally updating the children entities.
If the ?nested
query parameter and the COLLECTIONS
attribute are both
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.
The ?nested
semantics MUST be applied to all nested COLLECTIONS
attributes
within the request regardless of the level in the hierarchy in which they
appear.
For example:
PUT https://example.com/endpoints/ep1?nested
{
"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, regardless of the presence (or absence) of the
?nested
query parameter.
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?nested
{
"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 this 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 entities
Based on the entity being processed, the OPTIONS
available will vary.
The PUT
variant MUST adhere to the following:
- The URL MUST be of the form:
PATH-TO-ENTITY
. - The HTTP body MUST contain the full updated serialization of the entity to be processed.
- The entity processed MUST either be created (if it does not already exist), or updated (if it does exist).
- Any mutable attribute which is either missing or present with a value of
null
, MUST be interpreted as a request to delete the attribute. - Excluding any Registry collection attributes, all mutable attributes specified MUST be a full serialization of the attribute. Any missing nested attribute MUST be interpreted as a request to delete the attribute.
The POST
variant MUST adhere to the following:
- The HTTP body MUST contain a JSON map where the key MUST be the
SINGULARid
of each entity in the map. Note, that in the case of a map of Versions, theversionid
is used instead. - Each value in the map MUST be the full serialization of the entity to be
either added or updated. Note that
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. - The processing of each individual entity in the map MUST follow the same
rules as defined for
PUT
above.
The PATCH
variant when directed at a single entity, MUST adhere to the PUT
semantics defined above with the following exceptions:
- Any mutable attribute which is missing MUST be interpreted as a request to
leave it unchanged. However, modifying some other attribute (or some other
server semantics) MAY modify it. A value of
null
MUST be interpreted as a request to delete the attribute. - When processing a Resource or Version, that has its
hasdocument
model aspect set totrue
, the URL accessing the entity MUST include the$structure
suffix, and MUST generate an error in the absence of the$structure
suffix. This is because when it is absent, the processing of the HTTPxRegistry-
headers are already defined with "patch" semantics so a normalPUT
orPOST
can be used instead. UsingPATCH
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.
The PATCH
variant when directed at an xRegistry collection, MUST adhere to
the following:
- The HTTP body MUST contain a JSON map where the key MUST be the
SINGULARid
of each entity in the map. Note, that in the case of a map of Versions, theversionid
is used instead. - Each value in the map MUST contain just the attributes that are to be
updated for that entity. See
PATCH
semantics when directed as a single entity above. - The processing of each individual entity in the map MUST follow the same
rules as defined for
PATCH
of a single entity above.
The processing of each individual entity follows the same set of rules:
- If an entity with the specified
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. - See the definition of each attribute for the rules governing how it is processed.
- All attributes present MUST be a full representation of its value. This means any complex attributes (e.g. object, maps), MUST be fully replaced by the incoming value.
- A request to update, or delete, a read-only attribute MUST be silently
ignored. However, a request that includes a
SINGULARid
MUST be compared with the entity's current value and if it differs then an error MUST be generated. This include bothRESOURCEid
andversionid
in the case of Resources and Versions. This is to prevent accidentally updating the wrong entity. - A request to update a mutable attribute with an invalid value MUST generate an error (this includes deleting a mandatory mutable attribute).
- Any Registry collection attributes MUST be processed per the rules specified in the Updating Nested Registry Collections section.
- Any error during the processing of an entity, or its nested entities, MUST result in the entire request being rejected and no updates performed.
A successful response MUST return the same response as a GET
to the entity
(or entities) processed without any query parameters, showing their current
representation, with the following exceptions:
- In the
POST
case, or aPATCH
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. - In the
PUT
orPATCH
cases that are directed to a single entity, for a newly created entity, the HTTP status MUST be201 Created
, and it MUST include an HTTPLocation
header with a URL to the newly created entity. Note that this URL MUST be the same as theself
attribute of that entity.
Otherwise an HTTP 200 OK
without an HTTP Location
header MUST be returned.
Note that the response MUST NOT include any inlinable attributes (such as
nested Registry collections, or RESOURCE
/RESOURCEbase64
attributes).
To retrieve a Registry collection, an HTTP GET
MAY be used. The request
MUST be of the form:
GET PATH-TO-COLLECTION[?inline=...&filter=...&export]
The following query parameters SHOULD be supported by servers:
?inline
- See inlining for more information.?filter
- See filtering for more information.?export
- See exporting for more information.
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[?inline=...&filter=...&export]
The following query parameters SHOULD be supported by servers:
?inline
- See inlining for more information.?filter
- See filtering for more information.?export
- See exporting for more information.
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:
- to delete a single entity, an HTTP
DELETE
MAY be used. The request MUST be of the form:
DELETE PATH-TO-COLLECTION/ID-OF-ENTITY[?epoch=UINTEGER]
Where:
- The request body SHOULD be empty.
- If the entity can not be found, then an HTTP
404 Not Found
error MUST be generated. - In the case of deleting Resources, a
DELETE
directed to themeta
sub-object is not supported and MUST generate an HTTP405 Method Not Allowed
error in response.
The following query parameter MUST be supported by servers:
epoch
The presence of this query parameter indicates that the server MUST check to ensure that theepoch
value matches the entity's currentepoch
value and if it differs then an error MUST be generated.
- to delete multiple entities within a Registry collection the request MUST be of the form:
DELETE PATH-TO-COLLECTION
{
"KEY": { # SINGULARid of entity
"epoch": UINTEGER ?
# Remainder of entity serialization is ignored (except its id)
} *
} ?
Where:
- If the request body is empty (no map), then all entities in the collection MUST be deleted.
- If the request body is not empty, then it MUST be a map containing zero or
more entries where the key of each entry is each entity's unique
identifier - which is the
SINGULARid
of the entity. - If an
epoch
value is specified for an entity then the server MUST check to ensure that the value matches the entity's currentepoch
value and if it differs then an error MUST be generated. - If the entity's unique identifier is present in the object, then it MUST
match its corresponding
KEY
value. - Any other entity attributes that are present in the request MUST be silently ignored, even if their values are invalid.
- If one of the referenced entities can not be found then the server MUST silently ignore this condition and not treat it as an error.
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",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"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 responses and document view, otherwise OPTIONALself
- REQUIRED in responses, otherwise OPTIONALepoch
- REQUIRED in responses, otherwise OPTIONALname
- OPTIONALdescription
- OPTIONALdocumentation
- OPTIONALlabels
- OPTIONALcreatedat
- REQUIRED in responses, otherwise OPTIONALmodifiedat
- REQUIRED in responses, otherwise OPTIONAL
and the following Registry level attributes:
- Type: String
- Description: The version of this specification that the serialization adheres to
- Constraints:
- REQUIRED in responses, OPTIONAL in requests.
- REQUIRED in document view.
- MUST be a read-only attribute in API view.
- If present, MUST be non-empty.
- Examples:
1.0
- Type: Registry Model
- Description: A description of the features, extension attributes, Groups and Resources supported by this Registry. See Registry Model
- Constraints:
- OPTIONAL.
- MUST NOT be included in responses unless requested.
- MUST be included in responses if requested.
- SHOULD be included in document view when the model is not known in advance.
- Type: Set of Registry Collections
- Description: A list of Registry collections that contain the set of Groups supported by the Registry.
- Constraints:
- REQUIRED in responses, MAY be present in requests.
- REQUIRED in document view.
- If present, it MUST include all nested Group Collection types in the Registry, even if some of the collections are empty.
To retrieve the Registry, its metadata attributes, and Groups, an HTTP GET
MAY be used.
The request MUST be of the form:
GET /[?specversion=...&inline=...&filter=...&export]
The following query parameters SHOULD be supported by servers:
specversion
The presence of this OPTIONAL query parameter indicates that the response MUST adhere to the xRegistry specification version specified. If the version is not supported then an error MUST be generated. Note that this query parameter MAY be included on any API request to the server not just the root of the Registry. When not present, the default value is the newest version of this specification supported by the server.?inline
- See inlining for more information.?filter
- See filtering for more information.?export
- See exporting for more information.
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
ETag: "UINTEGER"
{
"specversion": "STRING",
"registryid": "STRING",
"self": "URL",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"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
ETag: "1"
{
"specversion": "0.5",
"registryid": "myRegistry",
"self": "https://example.com/",
"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:
- The request asks for the model to be included in the response.
- The request asks for the
schemagroups
Group to be inlined in the response. - The
endpoints
Group has one extension attribute defined.
GET /?inline=schemagroups,model
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
ETag: "1"
{
"specversion": "0.5",
"registryid": "myRegistry",
"self": "https://example.com/",
"epoch": 1,
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"model": {
"schemas": [ "xRegistry-json", "jsonSchema/2020-12" ],
... 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 /[?nested]
or
PATCH /[?nested]
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 }, ? # Only if "?nested" is present
# Repeat for each Group type
"GROUPS": { GROUPS collection } ? # Only if "?nested" is present
}
Where:
- The HTTP body MUST contain the full JSON representation of the Registry entity's mutable attributes.
- The request MAY include the
'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 ofnull
MUST generate an error.
The following query parameter SHOULD be supported by servers:
?nested
- See Updating Nested Registry Collections for more information.
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
ETag: "UINTEGER"
{
"specversion": "STRING",
"registryid": "STRING",
"self": "URL",
"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
ETag: "2"
{
"specversion": "0.5",
"registryid": "myRegistry",
"self": "https://example.com/",
"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
}
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:
- Specification defined attributes that are
serverrequired
MUST NOT have this aspect changed tofalse
. - Specification defined attributes that are
readonly
andserverrequired
MUST NOT have thereadonly
aspect changed tofalse
. - Once an attribute is defined (per the specification or as an extension), the
name
andtype
aspects of the attribute MUST NOT be changed.
Changes to specification defined attributes MAY be included in the request. Any specification attributes not included in the request 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 an 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 reject model definitions that include unknown model language attributes.
Once a Registry has been created, implementations MAY choose to limit the types of changes made to the model - for example, to ensure backwards compatibility of clients or to ensure existing entities do not need to be changed to be consistent with the new model.
Implementations are REQUIRED to ensure that after any model changes are made, all of the entities in the Registry are valid with respect to the new model definition (including all Versions of Resources). How this is achieved will be implementation specific. For example, implementations can choose to automatically modify existing entities, or even to delete non-conforming entities (such as when Groups or Resource types are removed). However, it is STRONGLY RECOMMENDED that implementations not delete entities due to attribute modifications.
When model changes are made, it might be necessary to modify existing entity's attributes to ensure they are conformant with the new model. The following ordered suggestions are defined to provide consistency across implementations, but it is NOT REQUIRED for implementations to follow these rules if a different set of changes are more appropriate:
- If a
default
value is defined for the attribute, then it SHOULD be used. - If the attribute is OPTIONAL, then the attribute SHOULD be deleted.
- If an enum is defined and
strict
istrue
, the firstenum
value SHOULD be used. - If valid, the zero value for the attribute's type SHOULD be used:
- Array, Map, Object: empty value (e.g.
[]
,{}
,{}
respectively). - Boolean: false.
- Numeric: zero.
- String, URI, URIReference, URI-Template, URL: empty string (
""
). - Timestamp: zero (00:00:00 UTC Jan 1, 1970).
- Array, Map, Object: empty value (e.g.
- If valid, String attributes SHOULD use
"undefined"
.
If a backwards incompatible change is needed, and the existing entities need to be preserved, then it is RECOMMENDED that users define a new Group or Resource types for future instances of those entities.
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:
- as a stand-alone entity. 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.
- as part of the Registry contents. 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:
{
"schemas": [ "STRING" * ], ? # Available schema formats
"attributes": { # Registry level extensions
"STRING": { # Attribute name
"name": "STRING", # Same as attribute's key
"type": "TYPE", # boolean, string, array, 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
"clientrequired": BOOLEAN, ? # Default: false
"serverrequired": BOOLEAN, ? # Default: false
"default": VALUE, ? # Attribute's default value, scalars
"attributes": { ... }, ? # If "type" above is object
"item": { # If "type" above is map,array
"type": "TYPE", ? # map value type, or array type
"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"
"attributes": { ... }, ? # See "attributes" above
"resources": {
"STRING": { # Key=plural name, e.g. "messages"
"plural": "STRING", # e.g. "messages"
"singular": "STRING", # e.g. "message"
"maxversions": UINTEGER, ? # Num Vers(>=0). Default=0, 0=unlimited
"setversionid": BOOLEAN, ? # Supports client specified Version IDs
"setdefaultversionsticky": BOOLEAN, ? # Supports client "default" selection
"hasdocument": BOOLEAN, ? # Has separate document. Default=true
"typemap": MAP, ? # contenttype mappings
"attributes": { ... }, ? # Version attributes/extensions
"metaattributes": { ... } ? # Resource attributes/extensions
} *
} ?
} *
} ?
}
The following describes the attributes of Registry model:
-
schemas
- A list of schema formats in which the Registry model can be returned. Each
value MUST be a schema document format name (e.g.
jsonSchema/2020-12
), and SHOULD be of the formNAME[/VERSION]
. All implementations of this specification MUST supportxRegistry-json
(the JSON serialization as defined by this specification), and SHOULD include it in the list. - Type: String.
- OPTIONAL.
- MUST be case insensitive.
- MUST be a read-only attribute in API view.
- A list of schema formats in which the Registry model can be returned. Each
value MUST be a schema document format name (e.g.
-
attributes
- The set of attributes defined at the indicated level of the Registry. This includes extensions and specification defined/modified attributes.
- Type: Map where each attribute's name MUST match the key of the map.
- REQUIRED at specification defined locations, otherwise OPTIONAL for extensions Objects.
-
attributes."STRING"
- The name of the attribute being defined. See
attributes."STRING".name
for more information. - Type: String.
- REQUIRED.
- The name of the attribute being defined. See
-
attributes."STRING".name
-
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 MUST be generated if one is present in a request.Often
*
is used with atype
ofany
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 theifvalues
feature, but a non-*
attribute MAY define anifvalues
attribute named*
as long as there isn't already one defined for this level in the entityAn 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.
-
Type: String.
-
REQUIRED.
-
-
attributes."STRING".type
- The "TYPE" of the attribute being defined. MUST be one of the data types (in lower case) defined in Attributes and Extensions.
- Type: TYPE.
- REQUIRED.
-
attributes."STRING".description
- A human readable description of the attribute.
- Type: String.
- OPTIONAL.
-
attributes."STRING".enum
-
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 thetype
is a scalar. See thestrict
attribute below.When specified without
strict
beingtrue
, this list is just a suggested set of values and the attribute is NOT REQUIRED to use one of them. -
Type: Array.
-
OPTIONAL.
-
-
attributes."STRING".strict
- Indicates whether the attribute restricts its values to just the array of
values specified in
enum
or not. A value oftrue
means that any values used that is not part of theenum
set MUST generate an error. This attribute has no impact whenenum
is absent or an empty array. - When not specified, the default value is
true
. - Type: Boolean.
- OPTIONAL.
- Indicates whether the attribute restricts its values to just the array of
values specified in
-
attributes."STRING".readonly
-
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 is
false
. When the attribute name is*
thenreadonly
MUST NOT be set totrue
. Note, bothclientrequired
andreadonly
MUST NOT be set totrue
at the same time. -
Type: Boolean.
-
OPTIONAL.
-
-
attributes."STRING".immutable
-
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
andSINGULARid
, 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 is
false
. -
Type: Boolean.
-
OPTIONAL.
-
-
attributes."STRING".clientrequired
-
Indicates whether this attribute is a REQUIRED field for a client when creating or updating an entity. When not specified the default value is
false
. When the attribute name is*
thenclientrequired
MUST NOT be set totrue
.During creation or update of an entity if this attribute is not specified then an error MUST be generated.
-
Type: Boolean.
-
OPTIONAL.
-
-
attributes."STRING".serverrequired
-
Indicates whether this attribute is a REQUIRED field for a server when serializing an entity. When not specified the default value is
false
. When the attribute name is*
thenserverrequired
MUST NOT be set totrue
. Whenclientrequired
istrue
thenserverrequired
MUST also betrue
. -
Type: Boolean
-
OPTIONAL
-
-
attributes."STRING".default
- This value MUST be used to populate this attribute's value if one was not provided by a client. An attribute with a default value does not mean that its owning Object is mandated to be present, rather the attribute would only appear when the owning Object is present. By default, attributes have no default values.
- Type: MUST be the same type as the
type
of this attribute and MUST only be used for scalar types. - OPTIONAL.
-
attributes."STRING".attributes
- This contains the list of attributes defined as part of a nested resource.
- Type: Object, see
attributes
above. - MAY be present when the owning attribute's
type
isobject
, otherwise it MUST NOT be present. It MAY be absent or an empty list if there are no defined attributes for the nestedobject
.
-
attributes."STRING".item
- Defines the nested resource that this attribute references. This
attribute MUST only be used when the owning attribute's
type
value ismap
orarray
. - Type: Object.
- REQUIRED when owning attribute's
type
ismap
orarray
.
- Defines the nested resource that this attribute references. This
attribute MUST only be used when the owning attribute's
-
attributes."STRING".item.type
- The "TYPE" of this nested resource.
- Type: TYPE.
- REQUIRED.
-
attributes."STRING".item.attributes
- See
attributes
above. - OPTIONAL, and MUST ONLY be used when
item.type
isobject
.
- See
-
attributes."STRING".item.item
- See
attributes."STRING".item
above. - REQUIRED when
item.type
ismap
orarray
.
- See
-
attributes."STRING".ifvalues
-
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 thesiblingattributes
MUST be included in the model as siblings to this attribute.If
enum
is not empty andstrict
istrue
then this map MUST NOT contain any value that is not specified in theenum
array.This aspect MUST only be used for scalar attributes.
All attributes defined for this
ifvalues
MUST be unique within the scope of thisifvalues
and MUST NOT match a named attributed defined at this level of the entity. If multipleifvalues
sections, at the same entity level, are active at the same time then there MUST NOT be duplicateifvalues
attributes names between thoseifvalues
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 additionalifvalues
definitions. -
Type: Map where each value of the attribute is the key of the map.
-
OPTIONAL.
-
-
groups
- The set of Group types supported by the Registry.
- Type: Map where the key MUST be the plural name (
groups.plural
) of the Group type (GROUPS
). - REQUIRED if there are any Group types defined for the Registry.
-
groups.singular
- The singular name of a Group type e.g.
endpoint
(GROUP
). - Type: String.
- REQUIRED.
- MUST be unique across all Group types in the Registry.
- MUST be non-empty and MUST be a valid attribute name with the exception that it MUST NOT exceed 58 characters (not 63).
- The singular name of a Group type e.g.
-
groups.plural
- The plural name of the Group type e.g.
endpoints
(GROUPS
). - Type: String.
- REQUIRED.
- MUST be unique across all Group types in the Registry.
- MUST be non-empty and MUST be a valid attribute name with the exception that it MUST NOT exceed 58 characters (not 63).
- The plural name of the Group type e.g.
-
groups.attributes
- See
attributes
above.
- See
-
groups.resources
- The set of Resource types defined for the Group type.
- Type: Map where the key MUST be the plural name (
groups.resources.plural
) of the Resource type (RESOURCES
). - REQUIRED if there are any Resource types defined for the Group type.
-
groups.resources.singular
- The singular name of the Resource type e.g.
message
(RESOURCE
). - Type: String.
- REQUIRED.
- MUST be non-empty and MUST be a valid attribute name with the exception that it MUST NOT exceed 58 characters (not 63).
- MUST be unique within the scope of its owning Group type.
- The singular name of the Resource type e.g.
-
groups.resources.plural
- The plural name of the Resource type e.g.
messages
(RESOURCES
). - Type: String.
- REQUIRED.
- MUST be non-empty and MUST be a valid attribute name with the exception that it MUST NOT exceed 58 characters (not 63).
- MUST be unique within the scope of its owning Group type.
- The plural name of the Resource type e.g.
-
groups.resources.maxversions
- Number of Versions that will be stored in the Registry for this Resource type.
- Type: Unsigned Integer.
- OPTIONAL.
- The default value is zero (
0
). - A value of zero (
0
) indicates there is no stated limit, and implementations MAY prune non-default Versions at any time. - When the limit is exceeded, implementations MUST prune Versions by
deleting the oldest Version (based on creation times) first, skipping the
Version marked as "default". An exception to this pruning rule is if
maxversions
value is one (1
) then the newest Version of the Resource MUST always be the "default" and thesetdefaultversionsticky
aspect MUST befalse
.
-
groups.resources.setversionid
- Indicates whether support for client-side setting of a Version's
versionid
is supported. - Type: Boolean (
true
orfalse
, case sensitive). - OPTIONAL.
- The default value is
true
. - A value of
true
indicates the client MAY specify theversionid
of a Version during its creation process. - A value of
false
indicates that the server MUST choose an appropriateversionid
value during creation of the Version.
- Indicates whether support for client-side setting of a Version's
-
groups.resources.setdefaultversionsticky
- Indicates whether support for client-side selection of the "default" Version is supported for Resources of this type. Once set, the default Version MUST NOT change unless there is some explicit action by a client to change it - hence the term "sticky".
- Type: Boolean (
true
orfalse
, case sensitive). - OPTIONAL.
- The default value is
true
. - A value of
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. - A value of
false
indicates the server MUST choose which Version is the default Version. - This attribute MUST NOT be
true
ifmaxversions
is one (1
).
-
groups.resources.hasdocument
-
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 HTTPGET
to the Resource's URL will return the xRegistry metadata in the HTTP body. ThexRegistry-
HTTP headers MUST NOT be used for requests or response messages for these Resources. Use of$structure
on the request URLs MAY be used to provide consistency with the cases where this attribute is set totrue
- 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 HTTPGET
to the Resource MAY return an empty HTTP body. -
Type: Boolean (
true
orfalse
, case sensitive). -
OPTIONAL.
-
The default value is
true
. -
A value of
true
indicates that Resource of this type supports a separate document to be associated with it.
-
-
groups.resources.typemap
-
When a Resource's metadata is serialized in a response and the
?inline
feature is enabled, the server will attempt to serialize the Resource's "document" under theRESOURCE
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 customcontenttype
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). Thetypemap
attribute allows for this by defining a mapping ofcontenttype
values to well-known xRegistry format types.Since the
contenttype
value is a "media-type" per RFC9110, for purposes of looking it up in thetypemap
, just thetype/subtype
portion of the value (case insensitively) MUST be used. Meaning, anyparameters
MUST be excluded.If more than one entry in the
typemap
matches thecontenttype
, but they all have the same value, then that value MUST be used. If they are not all the same, thenbinary
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 theRESOURCEbase64
attribute, even if thecontenttype
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 theRESOURCE
attribute if it is valid JSON. Note that if there is a syntax error in the JSON then the server MUST treat the document asbinary
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 during the update of the xRegistry model.
By default, the following RFC9110
typemap
keys MUST be implicitly defined as follows, unless overridden by an explicittypemap
entry:application/json
: mapped tojson
*+json
: mapped tojson
text/plain
: mapped tostring
-
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.
-
Example:
"typemap": { "text/*": "string", "text/mine": "json" }
-
-
groups.resources.attributes
- The list of attributes associated with each Version of the Resource.
- See
attributes
above, andmetaattributes
below. - The list of
groups.resources.attributes
names MUST NOT overlap with the list ofgroups.resource.metaattributes
names.
-
groups.resources.metaattributes
- The list of attributes associated with the Resource, not its Versions,
and will appear in the
meta
sub-object of the Resource. - See
attributes
above. - The list of
groups.resources.attributes
names MUST NOT overlap with the list ofgroups.resource.metaattributes
names.
- The list of attributes associated with the Resource, not its Versions,
and will appear in the
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 model.schemas
attribute (discussed above) SHOULD 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:
- If specified, the
?schema
query parameter SHOULD be one of the validmodel.schema
values (case insensitive). Note that an implementation MAY choose to support a value that is not specified within themodel.schema
attribute. If not specified, the default value MUST bexRegistry-json
.
Implementations of this specification MUST support xRegistry-json
.
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: ...
... xRegistry model in a schema specific format ...
Where:
- The HTTP body MUST be a schema representation of the Registry model
in the format requested by the
?schema
query parameter. - If a
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 HTTP 400 Bad Request
error MUST be generated.
When the schema
is xRegistry-json
then the response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"attributes": {
"STRING": {
"name": "STRING",
"type": "TYPE",
"description": "STRING", ?
"enum": [ VALUE * ], ?
"strict": BOOLEAN, ?
"readonly": BOOLEAN, ?
"immutable": BOOLEAN, ?
"clientrequired": BOOLEAN, ?
"serverrequired": BOOLEAN, ?
"default": VALUE, ?
"attributes": { ... }, ?
"item": { ... }, ?
"ifvalues": {
"VALUE": {
"siblingattributes": { ... }
} *
} ?
} *
},
"groups": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"attributes": { ... }, ?
"resources": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"maxversions": UINTEGER, ?
"setversionid": BOOLEAN, ?
"setdefaultversionsticky": BOOLEAN, ?
"hasdocument": BOOLEAN, ?
"immutable": BOOLEAN, ?
"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
{
"schemas": [ "xRegistry-json", "jsonSchema/2020-12" ],
"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",
"attributes": {
... 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
{
"schemas": [ "STRING" * ], ?
"attributes": {
"STRING": {
"name": "STRING",
"type": "TYPE",
"description": "STRING", ?
"enum": [ VALUE * ], ?
"strict": BOOLEAN, ?
"readonly": BOOLEAN, ?
"immutable": BOOLEAN, ?
"clientrequired": BOOLEAN, ?
"serverrequired": BOOLEAN, ?
"default": VALUE, ?
"attributes": { ... }, ? # For nested object
"item": { ... }, ? # For nested map, array
"ifvalues": {
"VALUE": {
"siblingattributes": { ... }
} *
} ?
} *
},
"groups": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"attributes": { ... }, ? # See "attributes" above
"resources": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"maxversions": UINTEGER, ?
"setversionid": BOOLEAN, ?
"setdefaultversionsticky": BOOLEAN, ?
"hasdocument": BOOLEAN, ?
"typemap": MAP, ?
"attributes": { ... }, ? # Version attributes/extensions
"metaattributes": { ... } ? # Resource attributes/extensions
} *
} ?
} *
} ?
}
Where:
- The HTTP body MUST contain all of the attributes, Groups and Resources that the client wishes to define.
- Group and Resource types that are not present in the request MUST be interpreted as a request to delete them and all entities of those types MUST be deleted.
- Attributes not present in the request MUST be interpreted as a request to delete the attribute, and if the attribute is a specification defined attribute then it MUST be added back with its default definition.
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
{
"schemas": [ "STRING" * ], ?
"attributes": {
"STRING": {
"name": "STRING",
"type": "TYPE",
"description": "STRING", ?
"enum": [ VALUE * ], ?
"strict": BOOLEAN, ?
"readonly": BOOLEAN, ?
"immutable": BOOLEAN, ?
"clientrequired": BOOLEAN, ?
"serverrequired": BOOLEAN, ?
"default": VALUE, ?
"attributes": { ... }, ?
"item": { ... }, ?
"ifvalues": {
"VALUE": {
"siblingattributes": { ... }
} *
} ?
} *
},
"groups": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"attributes": { ... }, ?
"resources": {
"STRING": {
"plural": "STRING",
"singular": "STRING",
"maxversions": UINTEGER, ?
"setversionid": BOOLEAN, ?
"setdefaultversionsticky": BOOLEAN, ?
"hasdocument": BOOLEAN, ?
"typemap": MAP, ?
"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
{
"schemas": [ "xRegistry-json", "jsonSchema/2020-12" ],
"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:
- Each array value MUST be a reference to another Group/Resource plural
combination defined within the same Registry. It MUST NOT reference the
same Group under which the
ximport
resides. - An empty array MAY be specified, implying no Resources are imported.
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:
- The include path reference value MUST be compatible with the environment in which the include is being evaluated. For example, in an xRegistry server it would most likely always be a URL. However, in an external tool the reference might be to a local file on disk or a URL.
- The include MUST reference a JSON Object or Map that is consistent with
the model definition of where the include appears, and the target attributes,
or keys, are processed in place of the
$include
attribute. - Any attributes already present (as siblings to the include) MUST take
precedence over an included attribute - matching is done via comparing
the
name
of the attributes. - When
$includes
is used, the references MUST be processed in order and earlier attributes included take precedence over subsequently included attributes. - Both
$include
and$includes
MUST NOT be present at the same time at the same level in the model. - Included model definitions MAY use
include
directives, but MUST NOT be recursive. - Resolution of the include path MUST follow standard path resolution. Meaning, relative paths are relative to the document with the include directive.
- If present, the fragment (
#...
) 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 responses 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 an 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"
}
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",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"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/nested
}
Groups include the following common attributes:
GROUPid
- REQUIRED in responses and document view, otherwise OPTIONAL.self
- REQUIRED in responses, otherwise OPTIONAL.epoch
- REQUIRED in responses, otherwise OPTIONAL.name
- OPTIONAL.description
- OPTIONAL.documentation
- OPTIONAL.labels
- OPTIONAL.origin
- OPTIONAL.createdat
- REQUIRED in responses, otherwise OPTIONAL.modifiedat
- REQUIRED in responses, otherwise OPTIONAL.
and the following Group level attributes:
- Type: Set of Registry Collections.
- Description: A list of Registry collections that contain the set of Resources supported by the Group.
- Constraints:
- REQUIRED in responses, MAY be present in requests.
- REQUIRED in document view.
- If present in a response, it MUST include all nested Resource Collection types of the owning Group, even if some of the collections are empty.
To retrieve a Group collection, an HTTP GET
MAY be used.
The request MUST be of the form:
GET /GROUPS[?inline=...&filter=...&export]
The following query parameters SHOULD be supported by servers:
?inline
- See inlining for more information.?filter
- See filtering for more information.?export
- See exporting for more information.
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 ?
ETag: "UINTEGER"
{
"KEY": { # GROUPid
"GROUPid": "STRING",
"self": "URL",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"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",
"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",
"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
or POST
methods:
PUT /GROUPS/gID[?nested]
PATCH /GROUPS/gID[?nested]
PATCH /GROUPS[?nested]
POST /GROUPS[?nested]
The following query parameter SHOULD be supported by servers:
?nested
- See Updating Nested Registry Collections for more information.
The overall processing of these two APIs is defined in the Creating or Updating Entities section.
Each individual Group definition MUST adhere to the following:
{
"GROUPid": "STRING", ?
"self": "URL", ?
"epoch": UINTEGER, ?
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI" ?
"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 "?nested" is present
}
Each individual Group in a successful response MUST adhere to the following:
{
"GROUPid": "STRING",
"self": "URL",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"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 a 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[?inline=...&filter=...&export]
The following query parameters SHOULD be supported by servers:
?inline
- See inlining for more information.?filter
- See filtering for more information.?export
- See exporting for more information.
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
ETag: "UINTEGER"
{
"GROUPid": "STRING",
"self": "URL",
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"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
ETag: "1"
{
"GROUPid": "ep1",
"self": "https://example.com/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 attribute 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 an absolute URL to the Resource, and not to the "default" Version in theversions
collection. The Resource'sdefaultversionurl
attribute (in themeta
sub-object) can be used to locate the "default" Version.- A few extra attributes will appear to help with the navigation of the
Resource. For example, a URL to the
meta
sub-object and theversions
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 $structure
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$structure
will retrieve the xRegistry metadata information for the myschema
Resource.
When the Resource's path is appended with $structure
, 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 whichcontenttype
values are of the same format - see Registry Model for more information. If a Resource has a matchingcontenttype
but the contents of the Resource's document do not successfully parse (e.g. it'sapplication/json
but the JSON is invalid), thenRESOURCE
MUST NOT be used andRESOURCEbase64
MUST be used instead. -
RESOURCEurl
: this attribute MUST be used when the contents of the Resource's are stored within the Registry butRESOURCE
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 via $structure
, 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
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",
# Default Version attributes appear here
"metaurl": "URL",
"meta": { # Only if inlined/nested
"RESOURCEid": "STRING",
"self": "URL", # Resource URL, not Version
"xref": "URL", ? # Ptr to linked Resource
"epoch": UINTEGER, # Resource's epoch
"createdat": "TIMESTAMP", # Resource's
"modifiedat": "TIMESTAMP", # Resource's
"readonly": BOOLEAN, ? # Default is "false"
"compatibility": "STRING", ? # Default is "none"
# TODO - add timestamp?
"defaultversionid": "STRING",
"defaultversionurl": "URL",
"defaultversionsticky": BOOLEAN ? # Default is "false"
}, ?
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { map of Versions } # Only if inlined/nested
}
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 the metaurl
, versionsurl
and versionscount
attributes
MUST appear as HTTP headers and adhere to the following:
xRegistry-RESOURCEid: STRING
# 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.
The Resource level attributes include the following common attributes:
RESOURCEid
- REQUIRED in responses and document view, otherwise OPTIONAL.self
- REQUIRED in responses, otherwise OPTIONAL. MUST be an absolute URL to themeta
sub-object.epoch
- REQUIRED in responses, otherwise OPTIONAL.
and the following Resource level attributes:
- Type: Relative URL from base URL of Registry
- Description: indicates that this Resource is a reference to another Resource owned by another Group within the same Registry. See Cross Referencing Resources for more information.
- Type: Boolean
- Description: indicates whether this Resource is updateable by clients. This attribute is a server controlled attribute and therefore can not be modified by clients. This specification makes no statement as to when Resource are to be read-only.
- Constraints:
- When not present, the default value is
false
. - REQUIRED when
true
, otherwise OPTIONAL. - If present, it MUST be a case sensitive
true
orfalse
. - A request to update this attribute, on a non-read-only Resource, MUST be silently ignored.
- A request to update a read-only Resource MUST generate an error unless
the
?noreadonly
query parameter was used, in which case the error MUST be silently ignored. See Registry APIs for more information.
- When not present, the default value is
-
Type: String (with resource-specified enumeration of possible values)
-
Description: indicates whether Versions of this Resource need to adhere to a certain compatibility rule. For example, a "backward" compatibility value would indicate that all Versions of a Resource are mandated to be backwards compatible with the next oldest Version, as determined by their
createdat
timestamp attributes.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 thecompatibility
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 includenone
as one of the possible values and when set tonone
then compatibility checking MUST NOT be performed.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. This includes rejecting requests to add/delete/modify Versions or update this attribute in such a way as to make the existing Versions incompatible due to the new value.
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:
- If present, it MUST be a case sensitive value from the model defined enumeration range.
- When not present, the implied default value is
none
.
- Type: 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:
- REQUIRED in responses and document view, OPTIONAL in requests.
- If present, MUST be non-empty.
- MUST be the
versionid
of the default Version of the Resource. - See the
defaultversionsticky
section above for how to process these two attributes.
- Examples:
1
,2.0
,v3-rc1
(v3's release candidate 1)
-
Type: URL
-
Description: an absolute URL to the default Version of the Resource.
The Version URL path MUST include
$structure
if the request asked for the serialization of the Resource metadata. This would happen when$structure
was used in the request, or when the Resource is included in the serialization of a Group, such as when the?inline
feature is used. -
Constraints:
- REQUIRED in responses, OPTIONAL in requests.
- OPTIONAL in document view.
- MUST be a read-only attribute in API view.
- MUST be an absolute URL to the default Version of the Resource, and MUST.
be the same as the Version's
self
attribute.
-
Examples:
https://example.com/endpoints/ep1/messages/msg1/versions/1.0
-
Type: 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 oftrue
means that it has been explicitly set and the value ofdefaultversionid
MUST NOT automatically change if newer Versions are added. A value offalse
means the default Version MUST be the newest Version based oncreatedat
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.Note: if there is more than one Version with the newest
createdat
value, then the Version chosen by the server will be indeterminate. -
Constraints:
- When not present, the default value is
false
. - REQUIRED when
true
, otherwise OPTIONAL. - If present, it MUST be a case sensitive
true
orfalse
. - If present in a request, a value of
null
has the same meaning as deleting the attribute, implicitly setting it tofalse
. - Since this attribute and
defaultversionid
are closely related, the processing of them in a request message MUST adhere to the following:- The
defaultversionsticky
attribute is applied first. As a reminder, in thePATCH
case, if this attribute is missing in the request then this attribute remains unchanged in the Resource. - If the resulting value of this attribute is
false
then the sticky aspect MUST be turned off, and anydefaultversionid
in the request MUST be ignored. The newest Version MUST be the default Version. - If the resulting value of this attribute is
true
then the sticky aspect MUST be turned on, and anydefaultversionid
attribute from the request is applied - where a value ofnull
means "newest". If the request was aPUT
orPOST
and did not have adefaultversionid
then the implicit value ofnull
MUST be used, resulting in "newest". If the request was aPATCH
and did not have adefaultversionid
then the current default Version MUST be used. A reference to a Version that does not exist MUST generate an error.
- The
- When not present, the default value is
-
Examples:
true
,false
- Type: Object
- Description: an object that contains most of the Resource level attributes.
- Constraints:
- If present, it MUST appear as an attribute of the Resource as a sibling to the Resource's default Version attributes.
- REQUIRED in responses only if requested by the client.MUST NOT appear unless requested by the client.
- OPTIONAL in requests.
- REQUIRED in document view.
- The
meta
sub-object is an entity unto itself, meaning it supports theGET
,PUT
andPATCH
APIs as described for all entities within the xRegistry. It also has its ownepoch
value which adheres to the normalepoch
processing rules already described, and its value is only updated when themeta
attributes are updated.
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. If not present, 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. The presence of the
meta
attribute does not need to use the ?nested
query parameter.
- Type: URL
- Description: an absolute URL to the Resource's
meta
sub-object. This sub-object will contain most of the Resource level attributes. - Constraints:
- If present, it MUST appear as an attribute of the Resource as a sibling to the Resource's default Version attributes.
- REQUIRED in responses, OPTIONAL in requests.
- OPTIONAL in document view.
- MUST be a read-only attribute in API view.
- MUST be an absolute URL to the Resource's
meta
sub-object.
- Examples:
https://example.com/endpoints/ep1/messages/msg1/meta
- Type: Registry Collection
- Description: A map of Versions of the Resource.
- Constraints:
- REQUIRED in responses, MAY be present in requests.
- REQUIRED in document view.
- If present, it MUST always have at least one Version.
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:
- Operations directed to the URL of a Resource are actually acting upon the "default" Version of that Resource.
- Management of the Resource's xRegistry metadata needs to be separate from the management of any particular Version of the Resource, as well as any potential domain-specific document associated with the Resource/Version.
- The actions, and learning, necessary for end-users to access the most likely "data of interest" needs to be minimal.
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:
- As its underlying domain specific document.
- As its xRegistry metadata.
Which variant is used is controlled by the use of $structure
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 $structure
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 MUST appear as HTTP headers where the header name is the name of the
attribute prefixed with xRegistry-
. 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
).
Top-level map attributes whose values are of scalar types MUST also appear as
HTTP headers (each key having it's 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, $structure
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-epoch: UINT # Start default Version's attributes
xRegistry-name: STRING ?
xRegistry-isdefault: true ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-origin: STRING ?
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
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:
- The
RESOURCEid
is theSINGULARid
of the Resource, not the default Version. - The
versionid
attribute MUST be the ID of the Resource's default Version. - The
self
URL references the Resource, not the default Version. - The serialization of the
labels
attribute is split into separate HTTP headers (one per label name). - The
versionsurl
andversionscount
Collection attributes are included, but not theversions
collection itself. - The
Location
header only appears in response to a create operation and MUST be the same as theself
URL. - The
Content-Location
header MAY appear, and if present, MUST reference the "default" Version. Content-Disposition
SHOULD be present and if so, MUST be theRESOURCEid
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 would also appear as
xRegistry-
HTTP headers.
Appending $structure
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
# These are inherited from the default Version
"epoch": UINTEGER,
"name": "STRING", ?
"isdefault" true,
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"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/nested
"RESOURCEid": "STRING",
"self": "URL", # URL to "meta"
"xref": "URL", ? # Ptr to linked Resource
"epoch": UINTEGER, # Resource's epoch
"createdat": "TIMESTAMP", # Resource's
"modifiedat": "TIMESTAMP", # Resource's
"readonly": BOOLEAN, ? # Default is "false"
"compatibility": "STRING", ? # Default is "none"
"defaultversionid": "STRING",
"defaultversionurl": "URL",
"defaultversionsticky": BOOLEAN ? # Default is "false"
}, ?
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { Versions collection } ? # If inlined/nested
}
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 relative
URL to the target Resource. For example, a source Resource's meta
sub-object
defined as:
{
# Resource URL: http://example.com/groups/group1/resource/sourceresource
"RESOURCEid": "sourceresource",
"xref": "groups/group2/resources/targetresource"
}
means that this "sourceresource" references the "targetresource" from
"group2". When the source Resource is serialized (e.g. as a result of a
GET
), then all of the target Resource's attributes (except its RESOURCEid
)
will appear as if they were locally defined. So, if the target Resource was
defined as:
{
"resourceid": "targetresource",
"versionid": "v1",
"self": "http://example.com/groups/group2/resources/targetresource",
"epoch": 2,
"isdefault": true,
"createdat": "2024-01-01-T12:00:00",
"modifiedat": "2024-01-01-T12:01:00",
"metaurl": "http://example.com/groups/groups2/resource/targetresource/meta",
"versionscount": 1,
"versionsurl": "http://example.com/groups/group2/resources/targetresource/versions"
}
then the resulting serialization of the source Resource would be:
{
"resourceid": "sourceresource",
"versionid": "v1",
"self": "http://example.com/groups/group1/resources/sourceresource",
"epoch": 2,
"isdefault": true,
"createdat": "2024-01-01-T12:00:00",
"modifiedat": "2024-01-01-T12:01:00",
"metaurl": "http://example.com/groups/group1/resource/sourceresource/meta",
"meta": {
"resourceid": "sourceresource",
"self": "http://example.com/groups/groups2/resource/sourceresource/meta",
"xref": "groups/group2/resources/targetresource",
"createdat": "2024-01-01-T12:00:00",
"modifiedat": "2024-01-01-T12:01:00"
},
"versionscount": 1,
"versionsurl": "http://example.com/groups/group1/resources/sourceresource/versions"
}
Note:
- The
RESOURCEid
of the source Resource MAY be different from theRESOURCEid
of the target Resource. - The
xref
attribute MUST appear within themeta
sub-object so a client can easily determine that this Resource is a cross-referenced Resource, and provide a URL to that targeted Resource (if ever needed). - All calculated attributes (such as the
defaultversionurl
andversionsurl
) MUST use theRESOURCEid
of the source Resource not the target Resource. In this respect, users of this serialization would never know that this is a cross-referenced Resource except for the presence of thexref
attribute. - The URL of the target Resource MUST be a relative URL of the form:
GROUPS/gID/RESOURCES/rID
, and MUST reference a Resource within the same Registry. However, it MUST NOT point to itself.
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. Only the
RESOURCEid
and xref
attributes are persisted for the source Resource, the
target Resource's attributes are inherited and not meant to be persisted
with the source Resource. This means that a write-operation on the source
Resource that includes the xref
attribute SHOULD NOT include any target
Resource attributes (or nested collections) and if present they MUST be
ignored. 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.
A request that deletes the xref
attribute MUST be interpreted as a request
to convert the Resource from a cross-reference Resource into a normal
Resource - in which case the normal semantics of updating the mutable
attributes apply. And any relationship with the target Resource is deleted.
Likewise, the presence of xref
on a write request can be used to convert a
normal Resource into a cross reference one, and in which case any existing
attributes on the source Resource MUST be deleted (except for RESOURCEid
and
xref
).
The xref
attribute MUST be a relative URL from the base URL of the Registry,
and MUST NOT start with a /
, while the base URL of the Registry MUST end
with a /
. This allows for simple concatenation of the two URLs to derive
the full URL of the target Resource.
If the target Resource itself is a cross reference Resource, then inlining
of the target Resource's attributes MUST NOT be done when serializing the
source Resource. Recursive, or transitively, following of xref
URLs it 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. 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/RESOURCES
PATCH /GROUPS/gID/RESOURCES
- Creates or updates one or more Resources.
PUT /GROUPS/gID/RESOURCES/rID[$structure]
PATCH /GROUPS/gID/RESOURCES/rID$structure
- Creates a new Resource, or update the default Version of a Resource.
POST /GROUPS/gID/RESOURCES/rID[$structure]
PATCH /GROUPS/gID/RESOURCES/rID$structure
- Creates or updates a single Version of a Resource.
PUT /GROUPS/gID/RESOURCES/rID/meta
PATCH /GROUPS/gID/RESOURCES/rID/meta
- Updates the
meta
sub-object of a Resource.
POST /GROUPS/gID/RESOURCES/rID/versions
PATCH /GROUPS/gID/RESOURCES/rID/versions
- Creates or updates one or more Versions of a Resource.
PUT /GROUPS/gID/RESOURCES/rID/versions/vID[$structure]
PATCH /GROUPS/gID/RESOURCES/rID/versions/vID$structure
- Creates or updates a single Version of a Resource.
And the delete APIs are summarized as:
DELETE /GROUPS/gID/RESOURCES
- Delete a list of Resources, or all if the list is absent.
DELETE /GROUPS/gID/RESOURCES/rID
- Delete a single Resource.
DELETE /GROUPS/gID/RESOURCES/rID/versions
- Delete a list of Versions, or all (and the Resource) if the list is absent.
DELETE /GROUPS/gID/RESOURCES/rID/versions/vID
- Delete a single Version of a Resource, and the Resource if last Version.
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[?inline=...&filter=...&export]
The following query parameters SHOULD be supported by servers:
?inline
- See inlining for more information.?filter
- See filtering for more information.?export
- See exporting for more information.
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
"epoch": UINTEGER, # Start of Default Ver attribs
"name": "STRING", ?
"isdefault": true,
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"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"
"xref": "URL", ? # Ptr to linked Resource
"epoch": UINTEGER, # Resource's epoch
"createdat": "TIMESTAMP", # Resource's
"modifiedat": "TIMESTAMP", # Resource's
"readonly": BOOLEAN, ? # Default is "false"
"compatibility": "STRING", ? # Default is "none"
"defaultversionid": "STRING",
"defaultversionurl": "URL",
"defaultversionsticky": BOOLEAN ?
}
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { Versions collection } ? # If inlined
} *
}
Where:
- The key of each item in the map MUST be the
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$structure",
"epoch": 1,
"name": "Blob Created",
"origin": "http://example.com",
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"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/RESOURCES[?nested]
PATCH /GROUPS/gID/RESOURCES[?nested]
Where:
- This API MUST create or update one or more Resources within the specified Group.
- The HTTP body MUST contain a map of Resources to be created or updated, serialized as xRegistry metadata.
PUT /GROUPS/gID/RESOURCES/rID[$structure][?nested]
PATCH /GROUPS/gID/RESOURCES/rID[$structure][?nested]
Where:
- These APIs MUST create or update a single Resource in the Group.
- When
$structure
is present, the HTTP body MUST be an xRegistry serialization of the Resource. - When
$structure
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[$structure][?setdefaultversionid=vID]
PATCH /GROUPS/gID/RESOURCES/rID$structure[?setdefaultversionid=vID]
Where:
- This API MUST create, or update, a single new Version of the specified Resource.
- When
$structure
is present, the HTTP body MUST be an xRegistry serialization of the Version that is to be created or updated. - When the
$structure
is absent, 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'sversionid
) MAY be included as HTTP headers.
PUT /GROUPS/gID/RESOURCES/rID/meta
PATCH /GROUPS/gID/RESOURCES/rID/meta
Where:
- This API MUST update the
meta
sub-object of the specified Resource. - As with all update operations, if the incoming request includes an
epoch
value that does not match the existingmeta
epoch
value then an error MUST be generated.
POST /GROUPS/gID/RESOURCES/rID/versions[?setdefaultversionid=vID]
PATCH /GROUPS/gID/RESOURCES/rID/versions[?setdefaultversionid=vID]
Where:
- This API MUST create or update one or more Versions of the Resource.
- The HTTP body MUST contain a map of Versions to be create or updated,
serialized as xRegistry metadata. Note that the map key of each entry
MUST be the Version's
versionid
not the Resource's. - If the Resource does not exist prior to this operation, it MUST be implicitly
created, the following rules apply:
- If there is only one Version created, then it MUST become the default
Version, and use of the
?setdefaultversionid
query parameter is OPTIONAL. - If there is more than one Version created, then use of the
setdefaultversionid
is REQUIRED and MUST be set to theversionid
of one of the specified Versions. - There MUST be at least one Version specified in the HTTP body. In other words, an empty collection MUST generate an error since creating a Resource with no Versions would immediate delete that Resource.
- If there is only one Version created, then it MUST become the default
Version, and use of the
See Default Version of a Resource for more
information about the ?setdefaultversionid
query parameter.
PUT /GROUPS/gID/RESOURCES/rID/versions/vID[$structure][?setdefaultversionid=vID]
PATCH /GROUPS/gID/RESOURCES/rID/versions/vID[$structure][?setdefaultversionid=vID]
Where:
- This API MUST create or update single Version in the Resource.
- When
$structure
is present, the HTTP body MUST contain the xRegistry metadata serialization of the Version. - When
$structure
is absent, the HTTP body MUST contain the Version's document.
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 can not be implicitly created, and would need to be
created manually.
When specified as an xRegistry JSON object, each individual Resource or Version in the request MUST adhere to the following:
{
"RESOURCEid": "STRING", ?
"versionid": "STRING", ?
"self": "URL",
"epoch": UINTEGER,
"name": "STRING", ? # Version level attributes
"isdefault": BOOLEAN, ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"createdat": "TIMESTAMP", ?
"modifiedat": "TIMESTAMP", ?
"contenttype": "STRING", ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If inlined & JSON
"RESOURCEbase64": "STRING", ? # If inlined & ~JSON
"metaurl": "STRING", # Resource level attributes
"meta": { # If ?nested is used
"RESOURCEid": "STRING",
"self": "URL",
"xref": "URL", ?
"epoch": UINTEGER,
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"readonly": BOOLEAN, ?
"compatibility": "STRING", ?
"defaultversionid": "STRING",
"defaultversionurl": "URL",
"defaultversionsticky": BOOLEAN ?
}
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { Versions collection } ? # If ?nested is used
}
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-self: URL
xRegistry-epoch: UINTEGER ?
xRegistry-name: STRING ?
xRegistry-isdefault: BOOLEAN ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-origin: STRING ?
xRegistry-createdat: TIMESTAMP ?
xRegistry-modifiedat: TIMESTAMP ?
xRegistry-RESOURCEurl: URL ?
xRegistry-metaurl: BOOLEAN ? # For Resources
xRegistry-versionsurl: URL ? # For Resources
xRegistry-versionscount: UINTEGER ? # For Resources
... 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 theRESOURCEurl
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 offalse
then the following rules apply:- Only the first form (serialization as a JSON Object) MUST be used.
- Use of the
$structure
suffix on the request URL is OPTIONAL and has no impact on processing beyond resulting in any URLs in the response being appended with$structure
if they reference this Resource (or its Versions). - Any request that includes the xRegistry HTTP headers MUST generate an error.
- An update request with an empty HTTP body MUST be interpreted as a request to delete all xRegistry mutable attributes - in essence, resetting the entity back to its default state.
- If the
versionid
attribute is present but it does not match the existing "default" Version'sversionid
(after any necessary processing of thedefaultversionid
attribute), then an error MUST be generated. Also see Default Version of a Resource. - When the xRegistry metadata is serialized as a JSON object, the processing
of the 3
RESOURCE
attributes MUST follow these rules:- At most only one of the 3 attributes MAY be present in the request, and the presence of any one of them MUST delete the other 2 attributes.
- If the entity already exists and has a document (not a
RESOURCEurl
), then absence of all 3 attributes MUST leave all 3 unchanged. - An explicit value of
null
for any of the 3 attributes MUST delete all 3 attributes (and any associated data). - When
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 thenRESOURCEbase64
MUST be used instead. - On a
PUT
orPOST
, whenRESOURCE
is present, if nocontenttype
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 acontenttype
value. - On a
PATCH
, whenRESOURCE
orRESOURCEbase64
is present, if nocontenttype
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 ($structure
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-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
}
Update default Version of a Resource as xRegistry metadata:
PUT /endpoints/ep1/messages/msg1$structure
Content-Type: application/json; charset=utf-8
ETag: "1"
{
"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
ETag: "2"
{
"messageid": "msg1",
"versionid": "1.0",
"self": "https://example.com/endpoints/ep1/messages/msg1$structure",
"epoch": 2,
"name": "Blob Created",
"isdefault": true,
"description": "a cool event",
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"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
for 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 HTTPLocation
header if the Resource has aRESOURCEurl
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 $structure
.
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-epoch: UINT
xRegistry-name: STRING ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-origin: STRING ?
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
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 theSINGULARid
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.- If
RESOURCEurl
is present then it MUST have the same value asLocation
. - If
Content-Location
is present then it MUST be a URL to the Version of the Resource in theversions
collection - same asdefaultversionurl
. Content-Disposition
SHOULD be present and if so, MUST be theRESOURCEid
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 $structure
appended to its URL path MAY be used.
The request MUST be of the form:
GET /GROUPS/gID/RESOURCES/rID$structure[?inline=...&filter=...&export]
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Location: URL ?
ETag: "UINTEGER"
{
"RESOURCEid": "STRING",
"versionid": "STRING",
"self": "URL", # URL to Resource, not default Ver
"epoch": UINTEGER,
"name": "STRING", ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"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
"xref": "URL", ?
"epoch": UINTEGER,
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"readonly": BOOLEAN, ?
"compatibility": "STRING", ?
"defaultversionid": "STRING",
"defaultversionurl": "URL",
"defaultversionsticky": BOOLEAN ?
},
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { Versions collection } ? # If inlined
}
Where:
RESOURCEid
MUST be the Resource'sSINGULARid
, not theversionid
of the default Version.self
is a URL to the Resource (with$structure
), not to the default Version of the Resource.RESOURCE
, orRESOURCEbase64
, MUST only be included if requested via the?inline
query parameter andRESOURCEurl
is not set.- If
Content-Location
is present then it MUST be a URL to the Version of the Resource in theversions
collection - same asdefaultversionurl
.
The following query parameters SHOULD be supported by servers:
?inline
- See inlining for more information.?filter
- See filtering for more information.?export
- See exporting for more information.
Examples:
Retrieve a message
Resource as xRegistry metadata:
GET /endpoints/ep1/messages/msg1$structure
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Location: https://example.com/endpoints/ep1/messages/msg1/versions/1.0
ETag: "1"
{
"messageid": "msg1",
"versionid": "1.0",
"self": "https://example.com/endpoints/ep1/messages/msg1$structure,
"epoch": 1,
"name": "Blob Created",
"isdefault": true,
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z",
"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 be 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",
"epoch": UINTEGER,
"name": "STRING", ?
"isdefault": BOOLEAN, ?
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"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 responses and document view, otherwise OPTIONAL. MUST be theRESOURCEid
of the owning Resource.versionid
- REQUIRED in responses and document view, otherwise OPTIONAL.self
- REQUIRED in responses, otherwise OPTIONAL - URL to this Version, not the Resource.epoch
- REQUIRED in responses, otherwise OPTIONAL. MUST be theepoch
value of the owning Resource.name
- OPTIONAL.description
- OPTIONAL.documentation
- OPTIONAL.labels
- OPTIONAL.origin
- OPTIONAL.createdat
- REQUIRED in responses, otherwise OPTIONAL.modifiedat
- REQUIRED in responses, otherwise OPTIONAL.contenttype
- OPTIONAL.
and the following Version level attributes:
isdefault
- REQUIRED whentrue
, otherwise OPTIONAL.RESOURCEurl
- OPTIONAL.RESOURCE
- OPTIONAL.RESOURCEbase64
- OPTIONAL.
as defined below:
- Type: String
- Description: An immutable unique identifier of the Version.
- Constraints:
- MUST be a non-empty string consisting of RFC3986
unreserved
characters (ALPHA / DIGIT / "-" / "." / "_" / "~"). - MUST NOT use a value of
null
orrequest
due to these being reserved for use by thesetdefaultversionid
operation. - MUST be case insensitive unique within the scope of the owning Resource.
- This attribute MUST be treated as case sensitive for look-up purposes.
This means that an HTTP URL request to an entity with the wrong case for its
versionid
MUST be treated as "not found". - In cases where the
versionid
is specified outside of the serialization of the Version (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 theversionid
outside of the entity, and it MUST be the same as the entity's existingversionid
if one exists, otherwise an error MUST be generated. - MUST be immutable.
- MUST be a non-empty string consisting of RFC3986
- Examples:
1.0
v2
v3-rc
TODO reuse SINGULARid section instead of duplicating this
Note, since versionid
is immutable, in order to change its value a new
Version would need to be created with the new versionid
that is a deep-copy
of the existing Version. Then the existing Version can be deleted.
-
Type: 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:
- REQUIRED in responses when the value is
true
, OPTIONAL whenfalse
. - REQUIRED in document view when the value is
true
, OPTIONAL whenfalse
. - If present, MUST be either
true
orfalse
, case sensitive. - If not present in responses, the default value MUST be
false
. - If present in requests, it MUST be silently ignored.
- REQUIRED in responses when the value is
-
Examples:
true
false
- Type: String
- Description: The media type of the entity as defined by RFC9110.
- Constraints:
- SHOULD be compliant with RFC9110.
- When serialized as an HTTP header, it MUST be named
Content-Type
notxRegistry-contenttype
like other xRegistry headers. - On an update request when the xRegistry metadata appears in HTTP headers,
unlike other attributes that will remain unchanged when not specified,
this attribute MUST be erased if the incoming request does not include
the
Content-Type
HTTP header. - This attribute MAY be specified even for Resources that use the
RESOURCEurl
attribute. While this specification can not guarantee that this attribute's value will match theContent-Type
returned by an HTTPGET
to theRESOURCEurl
, it is expected that they will match.
- Examples:
application/json
- Type: 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 all 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:
- REQUIRED if the Resource's document is not stored inside of the current Registry.
- If the document is stored in a network accessible endpoint then the
referenced URL MUST support an HTTP(s)
GET
to retrieve the contents. - MUST NOT be present if the Resource's
hasdocument
model attribute is set tofalse
.
- Type: 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 attribute, then this attribute MUST be used if the request asked for the document to be inlined in the response.
- Constraints
- MUST NOT be present when the Resource's Registry metadata is being serialized as HTTP headers.
- If the Resource's document is to be serialized and is not empty,
then either
RESOURCE
orRESOURCEbase64
MUST be present. - MUST only be used if the Resource document (bytes) is in the same format as the Registry Resource entity.
- MUST NOT be present if
RESOURCEbase64
is also present. - MUST NOT be present if the Resource's
hasdocument
model attribute is set to `false.
- Type: 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) are 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:
- MUST NOT be present when the Resource's Registry metadata is being serialized as HTTP headers.
- If the Resource's document is to be serialized and it is not empty,
then either
RESOURCE
orRESOURCEbase64
MUST be present. - MUST be a base64 encoded string of the Resource's document.
- MUST NOT be present if
RESOURCE
is also present. - MUST NOT be present if the Resource's
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 (by1
) unsigned integer starting with1
and is scoped to the owning Resource.- Each time a new
versionid
is needed, if an existing Version already has thatversionid
then the server MUST generate the nextversionid
value and try again. - The search for the next value does not restart with
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 three options for how this might be done:
-
Server's choice. If the Resource's model has the
setdefaultversionsticky
aspect set tofalse
then the server will always choose which Version is the "default" and any attempt by the client to control it MUST result in the generation of an error.This specification does not mandate the algorithm that the server uses, however the default choice SHOULD be "newest = default" (option 2).
-
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 is indeterminate. -
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 2 (newest = default)). 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 attribute),
a client MAY choose the "default" Version two ways:
- Via the Resource
defaultversionsticky
anddefaultversionid
attributes in itsmeta
sub-object. See Resource Attributes for more information about these attributes. - Via the
?setdefaultversionid
query parameter that is available on certain APIs, as defined below.
The ?setdefaultversionid
query parameter is defined as:
...?setdefaultversionid=vID
Where:
vID
is theversionid
of the Version that is to become the "default" version of the referenced Resource. A value ofnull
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 value, then including that value in the query parameter is not possible. In those cases a value ofrequest
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 MUST be generated.- If a non-null and non-request
vID
does not reference an existing Version of the Resource, after all Version processing is completed, then an HTTP400 Bad Request
error MUST be generated.
Any use of this query parameter on a Resource that has the
setdefaultversionsticky
aspect set to false
MUST generate an error.
Updating a Resource's defaultversionid
, regardless of the mechanism used to
do so, MUST adhere to the following rules:
- Aside from the special values of
null
andrequest
, its value MUST be theversionid
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. - When the operation also involves updating a Resource's "default" Version's attributes, the update to the default Version pointer MUST be done before the attributes are updated. In other words, the Version updated is the new default Version, not the old one.
- Choosing a new default Version of a Resource MUST NOT change any attributes
in any Resource's Versions, for example, attributes such as
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[?inline=...&filter=...&export]
The following query parameters SHOULD be supported by servers:
?inline
- See inlining for more information.?filter
- See filtering for more information.?export
- See exporting for more information.
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",
"epoch": UINTEGER,
"name": "STRING", ?
"isdefault": BOOLEAN,
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"contenttype": "STRING", ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If inlined & JSON
"RESOURCEbase64": "STRING" ? # If inlined & ~JSON
} *
}
Where:
- The key of each item in the map MUST be the
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$structure",
"epoch": 1,
"name": "Blob Created",
"isdefault": true,
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z"
}
}
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-epoch: UINT
xRegistry-name: STRING ?
xRegistry-isdefault: BOOLEAN ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-origin: STRING ?
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
Content-Disposition: STRING
... Version document ...
Where:
RESOURCEid
MUST be theSINGULARid
of the owning Resource.versionid
MUST be theSINGULARid
of the Version.self
MUST be a URL to the Version, not to the owning Resource.Content-Disposition
SHOULD be present and if so, MUST be theRESOURCEid
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-epoch: UINT
xRegistry-name: STRING ?
xRegistry-isdefault: BOOLEAN ?
xRegistry-description: STRING ?
xRegistry-documentation: URL ?
xRegistry-labels-KEY: STRING *
xRegistry-origin: STRING ?
xRegistry-createdat: TIME
xRegistry-modifiedat: TIME
xRegistry-RESOURCEurl: URL
Location: URL
Content-Disposition: STRING TODO: yes or let redirect do it?
Where:
Location
andRESOURCEurl
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-epoch: 2
xRegistry-name: Blob Created
xRegistry-isdefault: true
Content-Disposition: msg1
{
# Definition of a "Blob Created" event excluded for brevity
}
To retrieve a particular Version's metadata, an HTTP GET
with $structure
appended to its RESOURCEid
MAY be used.
The request MUST be of the form:
GET /GROUPS/gID/RESOURCES/rID/versions/vID$structure[?inline=...]
The following query parameter SHOULD be supported by servers:
A successful response MUST be of the form:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
ETag: "UINTEGER"
{
"RESOURCEid": "STRING",
"versionid": "STRING",
"self": "URL",
"epoch": UINTEGER,
"name": "STRING", ?
"isdefault": BOOLEAN,
"description": "STRING", ?
"documentation": "URL", ?
"labels": { "STRING": "STRING" * }, ?
"origin": "URI", ?
"createdat": "TIMESTAMP",
"modifiedat": "TIMESTAMP",
"contenttype": "STRING", ?
"RESOURCEurl": "URL", ? # If not local
"RESOURCE": ... Resource document ..., ? # If inlined & JSON
"RESOURCEbase64": "STRING" ? # If inlined & ~JSON
}
Where:
RESOURCEid
MUST be theSINGULARid
of the owning Resource.versionid
MUST be theSINGULARid
of the Version.self
MUST be a URL 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$structure
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
ETag: "2"
{
"messageid": "msg1",
"versionid": "1.0",
"self": "https://example.com/endpoints/ep1/messages/msg1/versions/1.0$structure",
"epoch": 2,
"name": "Blob Created",
"isdefault": true,
"createdat": "2024-04-30T12:00:00Z",
"modifiedat": "2024-04-30T12:00:01Z"
}
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
The ?inline
query parameter MAY be used on read requests to indicate how
nested collections/objects, or certain (potentially large) attributes, are to
be exposed in the response message.
The ?inline
query parameter on a GET
request indicates that the response
MUST include the contents of all specified inlinable attributes. Inlinable
attributes include:
- The
model
attribute on the Registry entity. - All Registry Collection types - e.g.
GROUPS
,RESOURCES
andversions
. - The
RESOURCE
attribute in a Resource or Version. - The
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 underendpoints
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
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 one 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. 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.
The ?nested
query parameter acts as the compliment to ?inline
in that
?nested
MUST be used on write requests to indicate that the client wishes
to have any inlinable attributes (except for RESOURCE*
) that are present in
the request processed. Absence of the ?nested
query parameter when an
inlinable attribute is present MUST generate an error.
Note: If the Registry can not return all expected data in one response because
it is too large then it MUST generate an HTTP 406 Not Acceptable
error and
SHOULD include an error message in the HTTP body indicating which attributes
are too large to be sent in one message. 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 ?filter
query parameter 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:
- If the collection is inlined it MUST only include entities that match the filter expression(s).
- The collection
url
attribute MUST include the appropriate filter expression(s) in its query parameters such that an HTTPGET
to that URL would return the same subset of entities. - The collection
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:
- All
EXPRESSION
values within the scope of one?filter
query parameter MUST be evaluated as a logicalAND
and any matching entities MUST satisfy all of the specified expressions within that?filter
query parameter. - The
?filter
query parameter can appear multiple times and if so MUST be evaluated as a logicalOR
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:
- For each
?filter
query parameter, find all entities that satisfy all expressions for that?filter
. Each will result in a sub-tree of entities. - After processing all individual
?filter
query parameters, combine those sub-trees into one result set and remove any duplicates - adjusting any collectionurl
andcount
values as needed.
The format of EXPRESSION
is:
[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 thePATH
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 asprop1['my.name'].prop2
ifmy.name
is the name of one attribute.PATH
MUST only consist of validGROUPS
,RESOURCES
orversions
, otherwise an error MUST be generated.ATTRIBUTE
MUST be the attribute in the entity to be examined.- Complex attributes (e.g.
labels
) MUST use dot (.
) to reference nested attributes. For example:labels.stage=dev
for a filter. - A reference to a nonexistent attribute SHOULD NOT generate an error and SHOULD be treated the same as a non-matching situation.
- When the equals sign (
=
) is present with aVALUE
thenVALUE
MUST be the desired value of the attribute being examined. Only entities whose specifiedATTRIBUTE
with thisVALUE
MUST be included in the response. - When the equals sign (
=
) is present without aVALUE
then the implied value is an empty string, and the matching MUST be as specified in the previous bullet. - When the equals sign (
=
) is not present then the response MUST include all entities that have theATTRIBUTE
present with any value. In database terms this is equivalent to checking for entities with a non-NULL value.
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:
- For boolean attributes, its value MUST be an exact case sensitive match
(
true
orfalse
). - For numeric attributes, it MUST be an exact match.
- For string attributes, it MUST be an exact match (case insensitive) when no
wildcard (
*
) character appears in theVALUE
. The presence of a wildcard indicates that any number of characters can appear at that location in theVALUE
. 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 aVALUE
of*
MUST be equivalent to checking for the 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* |
Same results as previous, with a different request URL |
/ | ?filter=endpoints.messages.versions.versionid=1.0 |
Only versions (and their owning endpoints.messages) 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 leaf entity from one of the sub-trees.
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 sub-tree is not changed by that search criteria.
The export semantics are designed to optimize the output for use by clients that want to retrieve a complete portion of the Registry's hierarchy with minimal duplication of information. Note that the export output can be used on a subsequent update/create operation.
During an export operation the following rules apply:
- All possible inlining MUST be performed. In other words, an implied
?inlining=*
MUST be in effect. The presence of the?inline
query parameter, even if set to*
MUST generate an error. - No filtering of the response entities is to be done. The presence of the
?filter
query parameter MUST generate an error. - All Resources are serialized as JSON objects - meaning, Resources with the
hasdoc
aspect set totrue
are serialized with implied$structure
semantics. - All Resources are serialized without the default Version attributes being
copied into the Resource. This is because they will already appear as
part of the
versions
collection so duplicating them is unnecessary. - Resources that are cross references (i.e. they have the
xref
attribute defined), MUST NOT include the target Resource's attributes or nested collections in its serialization.
There are two ways to export:
- The
?export
query parameter MAY be used on anyGET
request to any level of the Registry's hierarchy. - The
/export
API MAY be used with theGET
method to retrieve the entire Registry. This is semantically equivalent to aGET /?export
but expressed as an API so that a static file server implementation can support it.
As noted above, export changes the serialization rules of Resources by removing the default Version attributes. For clarity, the serialization of a Resource when exported will adhere to the following:
{
"RESOURCEid": "STRING",
"self": "URL",
"metaurl": "URL",
"meta": { ... },
"versionsurl": "URL",
"versionscount": UINTEGER,
"versions": { ... }
}
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 (U+0020).
- Double-quote (U+0022).
- Percent (U+0025).
- Any characters outside the printable ASCII range of U+0021-U+007E inclusive.
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:
- Encode the character using UTF-8, to obtain a byte sequence.
- Encode each byte within the sequence as
%xy
wherex
is a hexadecimal representation of the most significant 4 bits of the byte, andy
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 lower-case 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 be rejected. (For example, "%C0%A0" is an overlong encoding of U+0020, and MUST be rejected.)
Example: a header value of "Euro € 😀" SHOULD be encoded as follows:
- The characters, 'E', 'u', 'r', 'o' do not require encoding.
- Space, the Euro symbol, and the grinning face emoji require encoding. They are characters U+0020, U+20AC and U+1F600 respectively.
- The encoded HTTP header value is therefore "Euro%20%E2%82%AC%20%F0%9F%98%80" where "%20" is the encoded form of space, "%E2%82%AC" is the encoded form of the Euro symbol, and "%F0%9F%98%80" is the encoded form of the grinning face emoji.