xRegistry Service Model - Version 1.0-rc3 🔗

Abstract 🔗

This document describes the xRegistry model definition language that is used to define custom Groups and Resources for use within an xRegistry.

Readers are strongly encouraged to read the core xRegistry specification to learn about xRegistry itself prior to reading this specification.

Table of Contents 🔗

Overview 🔗

This specification defines the format and features of the xRegistry model language. The xRegistry model is used to define the custom Groups, Resources and attributes of the entities managed within an xRegistry service instance. It will also define the semantics, and constraints, of modifying an existing model.

Notations and Terminology 🔗

Notational Conventions 🔗

This specification adheres to the Notational Conventions defined in the core xRegistry specification.

Terminology 🔗

This specification uses the following terms as defined in the core xRegistry specification:

Registry Model 🔗

The Registry model defines the Groups, Resources, attributes and changes to specification-defined attributes that define what a Registry instance supports. This information is intended to be used by tooling that does not have knowledge of the structure of the Registry in advance and therefore will need to dynamically discover it.

The following sections will go into the details of how to create, retrieve and edit the model of a Registry, while the xRegistry protocol binding specifications will define how the operations defined in this specification will be mapped to those protocols.

The overall format of a model definition is as follows:

{
  "description": "<STRING>", ?
  "documentation": "<URL>", ?
  "labels": { "<STRING>": "<STRING>" * }, ?
  "attributes": {                      # Registry-level extensions
    "<STRING>": {                      # Attribute name
      "name": "<STRING>",              # Same as attribute's key
      "type": "<TYPE>",                # boolean, string, array, object, ...
      "target": "<XIDTYPE>", ?         # If "type" is "xid" or "url"
      "namecharset": "<STRING>", ?     # If "type" is "object"
      "description": "<STRING>", ?
      "enum": [ <VALUE> * ], ?         # Array of scalars of type "<TYPE>"
      "strict": <BOOLEAN>, ?           # Just "enum" values or not. Default=true
      "matchcase": <BOOLEAN>, ?        # Strings case-sensitive? Default=false
      "matchversions": <BOOLEAN>, ?    # Same for all Versions? Default=false
      "readonly": <BOOLEAN>, ?         # From client's POV. Default=false
      "immutable": <BOOLEAN>, ?        # Once set, can't change. Default=false
      "required": <BOOLEAN>, ?         # Default=false
      "default": <VALUE>, ?            # Scalar attribute's default value

      "attributes": { ... }, ?         # If "type" above is object
      "item": {                        # If "type" above is map,array
        "type": "<TYPE>", ?            # Map value type, or array type
        "target": "<XIDTYPE>", ?       # If this item "type" is xid/url
        "namecharset": "<STRING>", ?   # If this item "type" is object
        "attributes": { ... }, ?       # If this item "type" is object
        "item": { ... } ?              # If this item "type" is map,array
      }, ?

      "ifvalues": {                    # If "type" is scalar
        "<STRING>": {
          "siblingattributes": { ... } # Siblings to this "attribute"
        } *
      } ?
    } *
  },

  "groups": {
    "<STRING>": {                        # Key=plural name, e.g. "endpoints"
      "plural": "<STRING>",              # E.g. "endpoints"
      "singular": "<STRING>",            # E.g. "endpoint"
      "description": "<STRING>", ?
      "documentation": "<URL>", ?
      "icon": "<URL>", ?
      "labels": { "<STRING>": "<STRING>" * }, ?
      "modelversion": "<STRING>", ?      # Version of the group model
      "modelcompatiblewith": "<URI>", ?  # Statement of compatibility
      "attributes": { ... }, ?           # See "attributes" above
      "ximportresources": [ "<XIDTYPE>", * ], ?   # Include these Resources

      "constraints": {
        "<RESOURCES>.<PATH>": {          # Resource-plural + attribute path
          "default": <VALUE>, ?          # Group specific default
          "enum": [ <VALUE> + ], ?       # Allowed subset of values
          "equals": "<PATH>" ?           # Matching Group attribute path
        } *
      }, ?

      "resources": {
        "<STRING>": {                    # Key=plural name, e.g. "messages"
          "plural": "<STRING>",          # E.g. "messages"
          "singular": "<STRING>",        # E.g. "message"
          "description": "<STRING>", ?
          "documentation": "<URL>", ?
          "icon": "<URL>", ?
          "labels": { "<STRING>": "<STRING>" * }, ?
          "modelversion": "<STRING>", ?     # Version of the resource model
          "modelcompatiblewith": "<URI>", ? # Statement of compatibility
          "maxversions": <UINTEGER>, ?   # Num Vers(>=0). Default=0, 0=unlimited
          "setversionid": <BOOLEAN>, ?   # vid settable? Default=true
          "hasdocument": <BOOLEAN>, ?       # Has separate document. Default=true
          "versionmode": "<STRING>", ?      # 'ancestor' processing algorithm
          "singleversionroot": <BOOLEAN>, ? # Enforce single root. Default=false
          "validateformat": <BOOLEAN>, ?    # Do Version format checks. Default=false
          "validatecompatibility": <BOOLEAN>, ? # Do Version compat checks. Default=false
          "strictvalidation": <BOOLEAN>, ?  # Block unknown format/compat. Default=false
          "typemap": <MAP>, ?               # ContentType mappings
          "attributes": { ... }, ?          # Version attributes/extensions
          "resourceattributes": { ... }, ?  # Resource attributes/extensions
          "metaattributes": { ... } ?       # Meta attributes/extensions
        } *
      } ?
    } *
  } ?
}

The following describes the attributes of the Registry model:

description 🔗

labels 🔗

attributes 🔗

attributes.<STRING> 🔗

attributes.<STRING>.name 🔗

attributes.<STRING>.type 🔗

attributes.<STRING>.target 🔗

attributes.<STRING>.namecharset 🔗

attributes.<STRING>.description 🔗

attributes.<STRING>.enum 🔗

attributes.<STRING>.strict 🔗

attributes.<STRING>.matchcase 🔗

attributes.<STRING>.matchversions 🔗

attributes.<STRING>.readonly 🔗

attributes.<STRING>.immutable 🔗

attributes.<STRING>.required 🔗

attributes.<STRING>.default 🔗

attributes.<STRING>.attributes 🔗

attributes.<STRING>.item 🔗

attributes.<STRING>.item.type 🔗

attributes.<STRING>.item.target 🔗

attributes.<STRING>.item.namecharset 🔗

attributes.<STRING>.item.attributes 🔗

attributes.<STRING>.item.item 🔗

attributes.<STRING>.ifvalues 🔗

groups 🔗

groups.<STRING> 🔗

groups.<STRING>.plural 🔗

groups.<STRING>.singular 🔗

groups.<STRING>.description 🔗

groups.<STRING>.icon 🔗

groups.<STRING>.labels 🔗

groups.<STRING>.modelversion 🔗

groups.<STRING>.modelcompatiblewith 🔗

groups.<STRING>.attributes 🔗

groups.<STRING>.ximportresources 🔗

groups.<STRING>.constraints 🔗

The constraints map defines a set of rules that can be used to govern the attribute values in Resources that are added to instances of the Group type being defined.

These constraints MUST be applied to all instances of this Group type. Group instances MAY choose to add additional entries, or further restrict the ones defined here, via use of the Group instance's constraints attribute.

The constraints MUST be applied to all Versions of all Resources in the Group instances, and generate an error (constraint_failure) if a violation is detected.

When possible these constraints are best described when defining the Resource attributes in question, rather than here. However, there are cases where this might not be possible, such as when Resources are added to a Group type via the ximportresources feature or defining a relationship between Group and Resource attributes - such is the case for the equals constraint defined below.

Inclusion of a Resource in a Group instance via the meta.xref mechanism introduces some special considerations. The following describes the how the aspects of a constraint are applied to such xref'd Resources:

groups.<STRING>.constraints.<RESOURCES>.<PATH> 🔗

This map key MUST reference the Resource attribute that is to be constrained. It MUST reference a scalar attribute (top-level, or nested within objects) but but MUST NOT reference items in arrays or maps). It MUST only reference statically-defined attributes, not ones that are dynamically added via an ifvalues clause or via a * extension definition.

The <RESOURCES> portion of the map key MUST be the plural name of the Resource type being referenced.

The <PATH> portion of the map key MUST be a dot (.) notional traversal to the Resource attribute being constrained.

groups.<STRING>.constraints.<RESOURCES>.<PATH>.default 🔗

This aspect defines the default value that MUST be used for the referenced Resource attribute. This MUST override any default value specified in the model for that attribute. See the attribute default aspect for additional information concerning default value processing, as they apply here as well with one exception: adding a default value here does not mandate that the referenced Resource attribute's required aspect be set to true. However, it would have the same net effect at runtime because a value would always be defined for that attribute.

Note, if the constraint does not define a new enum set, but a default value is defined, then if the referenced attribute has an enum set and has its strict aspect set to true then this default value MUST be one of those enum values.

groups.<STRING>.constraints.<RESOURCES>.<PATH>.enum 🔗

This aspect defines the set of values that the referenced Resource attribute MUST be restricted to regardless of whether the Resource's strict attribute is set to true or not. The list's values MUST be valid per the Resource attribute's model definition (e.g. a proper subset of any enum defined if the Resource's enum is strict, and of the same type).

As with the enum attribute defined for attributes, an empty enum list in a constraints MUST be treated the same as no enum aspect at all and no further constraints on the allowable attribute values are applied beyond what the attribute itself defines.

If an enum set is defined, but a default values is not, then any default value specified in the attribute itself MUST be part of the enum set.

groups.<STRING>.constraints.<RESOURCES>.<PATH>.equals 🔗

Use of this aspect within a constraint MAY be used to ensure that all Resource instances within a Group instance have the same value for the specified attribute in all of their Versions.

When present, this aspect MUST contain the dot (.) notation path in the Group instance that the referenced Resource attribute MUST match. In the case of the attribute type being a string, the comparison MUST take into account the matchcase aspect of the attribute.

If the referenced Group attribute does not exist, then the equals constraint enforcement for the Resource attribute MUST be silently ignored.

If, after any potential default processing logic is performed, the Resource attribute (in any of its Versions) does not exist but the Group attribute does exist, then an error (constraint_failure) MUST be generated.

This attribute MUST reference a statically defined Group attribute. In other words, it can not reference an attribute defined by an ifvalues clause or a * extension definition. Nor can it reference an attribute within an array.

Note that if a Resource type's versioned attribute has a matchversions aspect set to false, then this feature will have the same net affect as matchversions for that attribute being true.

groups.<STRING>.resources 🔗

groups.<STRING>.resources.` 🔗

groups.<STRING>.resources.<STRING>.plural 🔗

groups.<STRING>.resources.<STRING>.singular 🔗

groups.<STRING>.resources.<STRING>.description 🔗

groups.<STRING>.resources.<STRING>.icon 🔗

groups.<STRING>.resources.<STRING>.labels 🔗

groups.<STRING>.resources.<STRING>.modelversion 🔗

groups.<STRING>.resources.<STRING>.modelcompatiblewith 🔗

groups.<STRING>.resources.<STRING>.maxversions 🔗

groups.<STRING>.resources.<STRING>.setversionid 🔗

groups.<STRING>.resources.<STRING>.hasdocument 🔗

groups.<STRING>.resources.<STRING>.versionmode 🔗

groups.<STRING>.resources.<STRING>.singleversionroot 🔗

groups.<STRING>.resources.<STRING>.validateformat 🔗

groups.<STRING>.resources.<STRING>.validatecompatibility 🔗

groups.<STRING>.resources.<STRING>.strictvalidation 🔗

groups.<STRING>.resources.<STRING>.typemap 🔗

groups.<STRING>.resources.<STRING>.attributes 🔗

groups.<STRING>.resources.<STRING>.resourceattributes 🔗

groups.<STRING>.resources.<STRING>.metaattributes 🔗


Clarifying the usage of the attributes, resourceattributes and metaattributes:

Retrieving the Registry Model 🔗

The Registry model is available in two forms:

The full "model" view can be thought of as a full schema definition of what the message exchanges with the server might look like. As such, it MUST include:

The "modelsource" view of the model is just what was provided by the user when the model was defined, or last edited. It is expected that this view of the model is much smaller than the full model and only includes domain-specific information. While specification-defined attributes MAY appear in this document, they are NOT RECOMMENDED since the server will automatically add them so users do not need to concern themselves with those details.

The modelsource document is always a semantic subset of the full model document.

xRegistry protocol binding specifications will typically define a way to retrieve these two model views as both stand-alone entities and inlined as part of the Registry entity.

For the sake of brevity, this specification doesn't include the full definition of the specification-defined attributes as part of the snippets of output. However, an example of a full model definition of a Registry can be can be found in this sample sample-model-full.json.

When retrieving the modelsource, the response MUST only include what was specified in the request to define the model - it MUST NOT include any auto-added specification defined metadata that will appear under model.

Creating or Updating the Registry Model 🔗

A server MAY support updating the model of a Registry via:

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:

Any specification attributes not included in a request to define, or update, a model MUST be included in the resulting full model. In other words, the full Registry's model consists of the specification-defined attributes overlaid with the attributes that are explicitly-defined as part of a modelsource update request.

Note: there is no mechanism defined to delete specification-defined attributes from the model.

Registries MAY support extension attributes to the model language (meaning, new attributes within the model definitions themselves), but only if the server supports them. Servers MUST generate an error (model_error) if a model definition includes unknown model language attributes.

Once a Registry has been created, changes to the model MAY be supported by server implementations. This specification makes no statement as to what types of changes are allowed beyond the following requirements:

Any request to update the model that does not adhere to those requirements MUST generate an error (model_compliance_error).

How the server guarantees that all entities in the Registry are compliant with the model is an implementation detail. For example, while it is NOT RECOMMENDED, it is valid for an implementation to modify (or even delete) existing entities to ensure model compliance. Instead, it is RECOMMENDED that the model update requests generate an error (model_compliance_error) if existing entities are not compliant.

For the purposes of validating that the existing entities in the Registry are compliant with the model, the mechanisms used to define the model (e.g. $include vs ximportresources vs defined locally) MUST NOT impact that analysis. In other words, model updates that have no semantic changes but rather switch between one of those 3 mechanisms MUST NOT invalidate any existing entities in the Registry.

Additionally, it is STRONGLY RECOMMENDED that model updates be limited to backwards compatible changes.

Implementations MAY choose to limit the types of changes made to the model, or not support model updates at all.

The xRegistry model definition used to create a sample xRegistry can be found here, while the resulting "full" model (with all of the system-defined aspects added) can be found here.

Reuse of Resource Definitions 🔗

When a Resource type definition is to be shared between Groups, rather than creating a duplicate Resource definition, the ximportresources mechanism MAY be used instead. The ximportresources attribute on a Group definition allows for a list of <XIDTYPE> references to other Resource types that are to be included within this Group.

For example, the following abbreviated model definition defines one Resource type (messages) under the messagegroups Group, that is also used by the endpoints Group.

"modelsource": {
  "groups": {
    "messagegroups": {
      "singular": "messagegroup",
      "resources": {
        "messages": {
          "singular": "message"
        }
      }
    },
    "endpoints": {
      "singular": "endpoint",
      "ximportresources": [ "/messagegroups/messages" ]
    }
  }
}

The format of the ximportresources specification is:

"ximportresources": [ "<XIDTYPE>", * ]

where:

Locally defined Resources MAY be defined within a Group that uses the ximportresources feature, however, Resource plural and singular values MUST be unique across all imported and locally defined Resources.

See Cross Referencing Resources for more additional information.

Includes in the xRegistry Model Data 🔗

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:

When the directives are used in a request to update the model, the server MUST resolve all includes prior to updating the model. The original (source) model definition, with any "include" directives, MUST be available via the modelsource attribute/entity. The expanded model (after the resolution of any includes, and after all specification-defined attributes have been added), MUST be available via the model attribute/entity. The directives MUST only be processed during the initial update of the model. In order to have them re-evaluated, a subsequent model update request (with those directive) MUST be sent.

When there is tooling used outside of the server, e.g. in an xRegistry client, if that tooling resolves the "include" directives prior to sending the model to the server, then the directives will not appear in the modelsource view of the the model. Ideally, tooling SHOULD allow users to choose whether the resolution of the directives are done locally or by the server.

Examples:

A model definition that includes xRegistry attributes from a file on a remote server, and adds the definition of one attribute to a Group named mygroups from an external Group named group1 in another xRegistry.

{
  "attributes": {
    "$include": "http://example.com/someattributes",
    "myattribute": {
      "name": "myattribute",
      "type": "string"
    }
  }
  "groups": {
    "mygroups": {
      "singular": "mygroup",
      "attributes": {
        "attr1": {
          "$include": "http://example.com/model#/groups/group1/attributes/attr1"
        }
        ... remainder of model excluded for brevity ...
      }
    }
  }
}

where http://example.com/someattributes might look like:

{
  "myattr": {
    "name": "myattr",
    "type": "string"
  }
}

and the second include target might look like:

{
  "name": "attr1",
  "type": "string"
}