Table of contents
- Design Guidelines
- Derived Guidelines
- Versioning
- Implementation principles
- HTTP headers
- Caching
- Idempotency
- Identify Caller
- Namespaces
- PATCH behaviour
- Error Handling
- Functional Errors and Error Codes
- Tracing across systems
- Authentication
- User Lookup
Design Guidelines
- Do not reinvent the wheel - Use existing concepts whenever possible (e.g. type system of OpenAPI, Problem details,…).
- Strive for a Level 3 of REST maturity.
- Use semantic versioning.
Derived Guidelines
- Whenever a resource returned in a response can contain embedded resources, the request must allow specifying whether and which embedded resources should be returned in full or as references.
- Follow Zalando RESTful API and Event Scheme guidelines
- Use of the JSON Problem element RFC9457. See also Errors and Problems.
- Standard Patch operations (RFC7396) - not JSON PATCH (RFC6902) (see PATCH behaviour)
- A resource is either represented in full or as a reference. The reference element has the name of the resource post-fixed with “Ref”. References normally only contains the URL to the referenced resource and a title element allowing to summarize the resource in one short string
- Although examples or recommendations are provided as to which information should best be represented in the title string, each implementor as the freedom to modify it to best suit his needs.
- Enumerations for very stable entities with limited set only, otherwise code lists. Stations codes are code lists. See also Catalog of code lists.
- Where possible, existing UIC code lists should be favored.
- Creation/ modification calls return the created/modified resource (not just an ok code)
Versioning
Semantic Versioning is used to mark the OSDM releases. OSDM strictly adheres to the semantics as deviation may hinder implementation project schedules for carriers or distributors.
- Major version is increased for any breaking change provided it is not a bug fix of an error that could not be implemented as valid behavior yet. All planned breaking changes are accumulated and a major version is usually developed each 2-3 years unless there is a industry or legal need for immediate breaking change, e.g. change in related open standards, change in technical regulation or change in consumer protection regulation.
- Minor version is increased for any planned release that doesn’t contain any breaking changes. Usually, there are 3-4 minor versions anually. Minor version doesn’t remove or changes API in incompatible way, it may only add new capabilities or mark current capabilities as deprecated.
- Patch version is increased for any bug fix of minor version, or backport of a feature from higher major/minor versions. Changes in patch versions retain all capabilities of the minor version and provides backport or bug fix. In terms of testing/certification, all patch versions may be treated as equal provided you check for the features that were corrected by the bug fix/added as backport from higher minor versions.
Backporting
Implementers may require to receive a newly requested feature to a lower version when there is project schedule in consideration. In such case, the backport is always provided as a new patch version of selected track. New patch versions are released for all minor version between the version where the feature was introduced and version to which it was requested by the implementers to be back-ported.
Backport is never applied to already released patch versions.
Version tracking
All versions are tracked in the master
branch of the git repository. Each major.minor version has its own folder where all patch versions of that minor version are stored.
Negotiating versions
A consumer can request a specific version of the API by providing the Accept
header with the following syntax:
Accept: application/osdm-json, version 3.5
.
All messages which contain a payload (both request and response payloads) should indicate the version of the
API used by providing a header Content-Type: application/osdm-json, version 3.5.0
The version indicated in the accept
header is typically a minor version (i.e. it has two digits, e.g. 3.5). The version indicated
in the Content-Type
header typically gives the full patch version (i.e. it has all three digits, e.g. 3.5.0).
Development
A pull request is expected to contain changes to the currently developed version.
If a backport is expected, pull request must provide files for a new patch version of all versions being patched.
Release
The openapi file is considered final when there is git tag and github release for given version that marks a respective commit where the final version of that file was saved.
Implementation principles
Implementations must follow the tolerant reader pattern. Schema validations must allow new attributes and objects. All extensible enums must not fail on items not proposed from the openapi or specification.
HTTP headers
In general, HTTP headers are used as specified in the relevant RFCs and as defined as the relevant section of the Zalando RESTful API and Event Scheme guidelines.
Specifically, within the OSDM API definition, the following headers are used:
Header name | Reference | Description |
---|---|---|
Accept | Negotiating versions | Select the API version |
AcceptLanguage | Interface language | Select the language(s) of the response |
Authorization | Authentication | Authenticate and authorize the user of the API |
Cache-Control | Caching | Caching control |
Content-Language | Interface language | Language of translatable strings in the payload |
Content-Type | Negotiating versions | Describes the API version of the payload (from 3.6) |
ETag | Large datasets | Version tag for caching response filed |
Expires | Caching | Expiration timestamp for caching |
IdempotencyKey | Idempotency | Add idempotency to endpoints which require this |
If-None-Match | Large datasets | Check against the ETag value given |
Location | Large datasets | Location (URN) of the dataset within the “See other” response |
Requestor | Identify caller | Distinguish between sales channels on the consumer side |
TraceParent | Tracing across systems | Used for tracing - see W3C specification |
TraceState | Tracing across systems | Used for tracing - see W3C specification |
x-accept-namespace | Namespaces | Negotiate (potentially proprietary) namespaces |
Caching
Some endpoints may give hints to the invoker on how long the retrieved data may be cached. They use the Cache-Control
and
Expires
headers for this purpose according to the relevant http standards.
This feature is primarily used for master data retrieval.
Large data sets
Some master data endpoints can return extremely large datasets. As this is impractical for a synchrounous http response, these endpoints
can return 303 See other
instead of 200 OK
and the data.
This is optional and has currently (as of OSDM Version 3.5) only been defined for GET /places
.
The behaviour is as described in the various HTTP standards and uses the headers ETag
, if-None-Match
, Location
This could be extended to other large and cacheable master data sets, e.g. products and coach layouts.
Idempotency
To add idempotency to endpoints which do not naturally have it. Endpoints with the HTTP verbs GET
and PUT
do not need this, as they
are naturally idempotent.
The idempotency header is generally added to endpoints with the POST
verb, and to endpoints with the PATCH
verb which change
state of underlying objects.
The IETF Draft is the relevant normative reference.
The general recommended behaviour is as follows:
- The consumer of the interface sets the value of the idempotency key header to a unique value, ideally a UUID
- Should the consumer perform a retry, the same value must be repeated in the idempotency key
-
When the provider receives the same request with a repeated idempotency key,
a. it must not repeat the changes to internal states of objects
b. it must return the identical answer as for the original request
- The provider needs to retain the idempotency key for a sufficient time, typically as long as the original request is allowed
Identify caller
Some providers offer certain tariffs only to online channels or to staffed sales channels. For this purpose, providers and consumers
can bilaterally agree on strings transported in the Requestor
header to identify the sales channel to the provider.
PATCH behaviour
As specified above, PATCH within OSDM is consistent with JSON Merge Patch (RFC7396). This means
- absent attributes are ignored (i.e. the attribute retains its previous value/state)
- null attributes will be removed
- present attributes will be updated
- new attributes will be added
Error Handling
In order to communicate errors to a consumer we support RFC 9457.
This RFC defines a “problem detail” as a way to carry machine- readable details of errors in a HTTP response to avoid the need to define new error response formats for HTTP APIs.
A problem details object can have the following members:
type
: A URI reference RFC3986 that identifies the problem type. This specification encourages that, when dereferenced, it provide human-readable documentation for the problem type (e.g., using HTML [W3C.REC-html5-20141028]). When this member is not present, its value is assumed to be “about:blank”.title
: A short, human-readable summary of the problem type. It SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization (e.g., using proactive content negotiation; see RFC7231, Section 3.4).status
: The HTTP status code (RFC7231, Section 6) generated by the origin server for this occurrence of the problem.detail
: A human-readable explanation specific to this occurrence of the problem.instance
: A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced.
Consumers MUST use the type
string as the primary identifier for the problem
type; the title
string is advisory and included only for users who are not
aware of the semantics of the URI and do not have the ability to discover them
(e.g., offline log analysis). Consumers SHOULD NOT automatically dereference the
type URI.
Functional Errors and Error Codes
In order that OSDM implementations behave consistently in error situations, a list of error codes and problem codes have been defined that must be supported in case of functional errors by all implementations.
Tracing across systems
To allow tracing across systems, the W3C has specified the
trace context. This functionality uses
the traceparent
and tracestate
headers to transport the relevant information.
In a chained implementation, each member of the chain needs to add their own
tracing information to the left of the incoming tracestate
value before sending
it further down the line.
The values transported here should be logged locally, so that when debugging across systems is required, log entries concerning the same invocation can be retrieved from both systems.
Authentication
The following three design principles are binding for each implementor:
-
Don’t reinvent crypto, thus we are using OAuth2.
-
The JWTs in use for the authentication should be short-lived (think of timeout duration single-digit multiples)
-
The JWTs sent by the consumer, regardless of where they are generated, must be digitally signed using a private key for which the provider is able to find the matching public key
These principles can be implemented as follows:
-
A registration service allows a consumer to register the necessary data (like: organization, technical admin, commercial admin, support line, other non-functional requirements like throttling limits) for approvement by the team providing access. any registered consumer will be approved or rejected if approved a unique, technical “ClientId” results and will be passed to the consumer
-
A login service allows to request a valid token (for e.g. OAuth2) by the registered ClientId and related secret per configuration the validity duration of the token may be set (for e.g. 10min.), after that the token must be renewed by the consumer optionally the token might be revoked (for e.g. if your devOps realizes the consumer does not behave as expected)
In multi-environments (like DEV, TEST, INT, PROD) consumers might register for each environment separately.
The detailed flow for machine-to-machine authentication can be found in Authentication.
User Lookup
From a devOps perspective it might by hard to control who uses your API (for
e.g. ClientId
and secret might by passed around or hacked).
The following issues should be defined outside of OSDM:
- Additional legal contracts with consumer
- IP white listing
- Mapping of related user properties to the technical
ClientId