diff --git a/.github/workflows/block-prs-from-release-5.yaml b/.github/workflows/block-prs-from-release-5.yaml index 99bc7bb..2584631 100644 --- a/.github/workflows/block-prs-from-release-5.yaml +++ b/.github/workflows/block-prs-from-release-5.yaml @@ -16,7 +16,7 @@ permissions: jobs: block-release-5: name: Block PRs from release/5.* - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: # Fail the workflow when the source branch starts with release/5. diff --git a/.github/workflows/check_links_lychee.yaml b/.github/workflows/check_links_lychee.yaml index b30805a..1e0c262 100644 --- a/.github/workflows/check_links_lychee.yaml +++ b/.github/workflows/check_links_lychee.yaml @@ -14,7 +14,7 @@ jobs: steps: # Checkout the repository. - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 # Check documentation links with Lychee. - uses: lycheeverse/lychee-action@v2 diff --git a/.github/workflows/lint_markdown.yaml b/.github/workflows/lint_markdown.yaml index 730b986..4f40d89 100644 --- a/.github/workflows/lint_markdown.yaml +++ b/.github/workflows/lint_markdown.yaml @@ -16,7 +16,7 @@ jobs: steps: # Checkout the repository. - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 # Run markdownlint on all documentation files. - name: Run markdownlint-cli2 diff --git a/.github/workflows/publish_release.yaml b/.github/workflows/publish_release.yaml index 3c50673..259e79e 100644 --- a/.github/workflows/publish_release.yaml +++ b/.github/workflows/publish_release.yaml @@ -14,7 +14,7 @@ jobs: steps: # Checkout this repository containing the documentation. - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 # Extract the version from the branch name (release/x.y → x.y). - name: Determine release version @@ -27,7 +27,7 @@ jobs: # Create a GitHub App token used to push to the publication repository. - name: Create GitHub App installation token id: app-token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@v3 with: app-id: ${{ vars.OOAPI_APP_PUBLISH_ID }} private-key: ${{ secrets.OOAPI_APP_PUBLISH_SECRET }} @@ -37,7 +37,7 @@ jobs: # Checkout the documentation website repository. - name: Checkout target repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: open-education-api/open-education-api.github.io token: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/publish_unreleased.yaml b/.github/workflows/publish_unreleased.yaml index 284569d..5af45cc 100644 --- a/.github/workflows/publish_unreleased.yaml +++ b/.github/workflows/publish_unreleased.yaml @@ -14,12 +14,12 @@ jobs: steps: # Checkout this repository containing the documentation. - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 # Create a GitHub App token used to push to the publication repository. - name: Create GitHub App installation token id: app-token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@v3 with: app-id: ${{ vars.OOAPI_APP_PUBLISH_ID }} private-key: ${{ secrets.OOAPI_APP_PUBLISH_SECRET }} @@ -29,7 +29,7 @@ jobs: # Checkout the documentation website repository. - name: Checkout target repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: open-education-api/open-education-api.github.io token: ${{ steps.app-token.outputs.token }} diff --git a/documentation/abbreviations.md b/documentation/abbreviations.md index c0f659e..073f143 100644 --- a/documentation/abbreviations.md +++ b/documentation/abbreviations.md @@ -23,9 +23,6 @@ | | and private organisations. | | Community | The OOAPI community consists of parties interested in OOAPI developments. Community members| | | are known by name and email address, and register with the community site themselves. | -| Slack | A Slack channel is available at: [openonderwijsapi.slack.com](https://openonderwijsapi.slack.com). Since access is on an invite | -| | basis, send an email to [info@openonderwijsapi.nl](mailto:info@openonderwijsapi.nl) and you will receive an invitation if you | -| | would like to participate in this Open Education API Slack channel. | | Project site | The website at [https://openonderwijsapi.nl/](https://openonderwijsapi.nl/). This website is used mainly to provide | | | clarification about the project to interested parties. | | | The site also provides access to the reference API, online documentation and the code | diff --git a/documentation/contact.md b/documentation/contact.md index c80a5a8..c5eeee6 100644 --- a/documentation/contact.md +++ b/documentation/contact.md @@ -11,12 +11,3 @@ Only the members of this mailing list may send email to this list. Send an email to [openonderwijsapi@list.surf.nl](mailto:openonderwijsapi@list.surf.nl) to contact the members of this mailing list. If you need support, contact: [info@openonderwijsapi.nl](mailto:info@openonderwijsapi.nl) - -## Slack - -A Slack channel is available at: -[openonderwijsapi.slack.com](https://openonderwijsapi.slack.com) Since access is -on an invitation basis, send an email to -[info@openonderwijsapi.nl](mailto:info@openonderwijsapi.nl) and you will receive -an invitation if you would like to participate on this Open education API slack -channel. diff --git a/documentation/technical/README.md b/documentation/technical/README.md index e1a33d2..1bbc663 100644 --- a/documentation/technical/README.md +++ b/documentation/technical/README.md @@ -14,10 +14,11 @@ Since OEAPI describes the information of an institution as resources, each insti The actual OEAPI specification is specified using the [OpenAPI Specification](https://www.openapis.org/), a standard that specifies how to specify (among other things) REST APIs. The detailed OEAPI specifications can be found here: -* [OEAPI v5](https://open-education-api.github.io/specification/v5/docs.html) (Current version) +* [OEAPI v6](https://openonderwijsapi.nl/specification/v6.0/) (Current version - fully supported) These are the previous versions: +* [OEAPI v5](https://open-education-api.github.io/specification/v5/docs.html) (limited support) * [OEAPI v4](https://open-education-api.github.io/specification/v4/docs.html) (limited support) * [OEAPI v3](https://open-education-api.github.io/specification/v3/docs.html) (unsupported) * [OEAPI v2](https://open-education-api.github.io/specification/v2/docs.html) (unsupported) diff --git a/documentation/technical/errorcodes.md b/documentation/technical/errorcodes.md index 84bbff6..ead854d 100644 --- a/documentation/technical/errorcodes.md +++ b/documentation/technical/errorcodes.md @@ -171,9 +171,37 @@ Servers SHOULD return an `Allow` header indicating supported methods. Returned when the server cannot provide a representation for the requested OEAPI or consumer version. -OEAPI uses explicit version negotiation rather than HTTP `Accept` headers. -If neither the requested version nor a lower compatible minor version is -supported, a `406` response MUST be returned. +OEAPI uses header-based version negotiation with an explicit request for a +single OEAPI version and, where applicable, a single consumer and consumer version. + +Clients MUST request exactly one OEAPI version via the `Accept` header and +MAY also include exactly one consumer and one consumer version. For example: + +```http +Accept: application/vnd.oeapi+json;version=6.0;consumer=mbo-oke-roster-service;consumer-version=2.0 +``` + +The server evaluates whether the requested OEAPI version or a compatible +minor version within the same major version can be provided. The same +principle applies to the optional consumer version: the server may return +the requested consumer version or a compatible minor version. + +If a compatible combination can be provided, the server returns the response +using the `Content-Type` header to indicate the actual OEAPI version and +consumer version used. For example: + +```http +Content-Type: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=6.1 +``` + +If no compatible version is available, a `406 Not Acceptable` response MUST +be returned. + +This behaviour differs from typical HTTP version negotiation: + +- exactly one explicit version is requested +- any compatible minor version may be returned +- the same principle applies to the optional consumer and consumer version This behaviour intentionally deviates from strict HTTP semantics to: @@ -183,29 +211,62 @@ This behaviour intentionally deviates from strict HTTP semantics to: ### Example – unsupported OEAPI version +Client: + +```http +POST /enrolments +Accept: application/vnd.oeapi+json;version=7.0;consumer=mbo-oke-roster-service;consumer-version=7.0 +``` + +Server: + +```http +HTTP/1.1 406 Not Acceptable +``` + +Response body: + ```json { "type": "https://api.example.org/problems/version-not-acceptable", "title": "Version not acceptable", "status": 406, - "detail": "The requested OEAPI version '5.0' cannot be served.", - "requestedVersion": "5.0", - "supportedVersions": ["6.1", "6.0"], + "detail": "The requested OEAPI version '7.0' cannot be served.", + "requestedVersion": "7.0", + "supportedVersions": ["5.0", "6.1"], "instance": "https://api.example.org/courses" } ``` ### Example – unsupported consumer version +Client: + +```http +POST /enrolments +Content-Type: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=6.1.1 +``` + +Server: + +```http +HTTP/1.1 406 Not Acceptable +``` + +Response body: + ```json { "type": "https://api.example.org/problems/version-not-acceptable", - "title": "Consumer version not acceptable", + "title": "Version not acceptable", "status": 406, - "detail": "The consumer version '2.0' is not supported.", - "requestedVersion": "2.0", - "supportedVersions": ["1.0", "0.94"], - "instance": "https://api.example.org/enrolments" + "detail": "The requested consumer version '6.1.1' cannot be served.", + "consumer": { + "consumerKey": "mbo-oke-roster-service" + }, + "requestedVersion": "6.1.1", + "supportedVersions": ["0.95", "1.0", "6.1"], + "instance": "https://api.example.org/courses" } ``` diff --git a/documentation/technical/versioning.md b/documentation/technical/versioning.md index 2fb2574..cb7917f 100644 --- a/documentation/technical/versioning.md +++ b/documentation/technical/versioning.md @@ -1,27 +1,35 @@ # Versioning: how it works -OEAPI uses an explicit, single-choice versioning model. +OEAPI uses a header-based, explicit, single-choice versioning model. -For each request, the client specifies exactly one OEAPI version and exactly one consumer version using HTTP headers. The server does not negotiate between alternatives. +For each request, the client specifies exactly one OEAPI version and +optionally one consumer with exactly one consumer version using the +`Accept` header. The server does not negotiate between alternatives. This is referred to as the *closed* versioning approach. In practice this means: -- the client sends one explicit OEAPI version via `Content-Type` -- the client sends one explicit consumer version via `OEAPI-Consumer-Version` -- the server either accepts that version, or falls back to a lower compatible minor version within the same major +- the client sends one explicit OEAPI version via `Accept` +- the client can include one consumer and one consumer version via `Accept` +- the server evaluates whether the requested version or a compatible minor + version within the same major can be provided (this may be lower or higher) +- the same principle applies to the optional consumer version - if no compatible version exists, the server rejects the request Only minor fallback is allowed. Major version fallback is never permitted. -The server MUST always indicate the actual versions used in the response headers. +The server always indicates the actual versions used in the response via +the `Content-Type` header. -If the requested version cannot be satisfied (and no compatible minor version is available), the server MUST return `406 Not Acceptable`, including the requested version and the list of supported versions in the response body. +If the requested version cannot be satisfied (and no compatible minor version +is available), the server returns `406 Not Acceptable`, including the +requested version and the list of supported versions in the response body. OEAPI deliberately does not use: -- HTTP `Accept` negotiation +- multiple alternative versions in the `Accept` header +- HTTP standard `Accept` negotiation - query parameters for versioning - version attributes in the request body @@ -37,20 +45,14 @@ Client: ```http GET /courses -Content-Type: application/vnd.OEAPI.v6.1+json - -OEAPI-Consumer-Name: mbo-oke-roster-service -OEAPI-Consumer-Version: 1.0 +Accept: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=6.0 ``` Server: ```http HTTP/1.1 200 OK -Content-Type: application/vnd.OEAPI.v6.1+json - -OEAPI-Consumer-Name: mbo-oke-roster-service -OEAPI-Consumer-Version: 1.0 +Content-Type: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=6.0 ``` The requested OEAPI and consumer versions are fully supported. @@ -58,41 +60,35 @@ The server returns exactly the same versions as requested. --- -### Example 2: minor fallback +### Example 2: minor fallback (higher or lower) Client: ```http GET /courses -Content-Type: application/vnd.OEAPI.v6.1+json - -OEAPI-Consumer-Name: mbo-oke-roster-service -OEAPI-Consumer-Version: 1.0 +Accept: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=6.0 ``` Server: ```http HTTP/1.1 200 OK -Content-Type: application/vnd.OEAPI.v6.0+json - -OEAPI-Consumer-Name: mbo-oke-roster-service -OEAPI-Consumer-Version: 0.94 +Content-Type: application/vnd.oeapi+json;version=6.0;consumer=mbo-oke-roster-service;consumer-version=6.1 ``` -Because both versions share the same major (6), the server falls back to the highest compatible minor and explicitly reports the versions used. +Because the versions share the same major (6), the server may return a +compatible minor version, higher or lower, and explicitly reports the +versions used. --- -### Example 3: unsupported version +### Example 3: unsupported OEAPI version Client: ```http POST /enrolments -Content-Type: application/vnd.OEAPI.v7.0+json - -OEAPI-Consumer-Version: 2.0 +Accept: application/vnd.oeapi+json;version=7.0;consumer=mbo-oke-roster-service;consumer-version=6.0 ``` Server: @@ -105,11 +101,52 @@ Response body: ```json { - "error": "Unsupported OEAPI or consumer version", - "requestedVersion": "2.0", - "supportedVersions": ["0.94", "1.0"] + "type": "https://api.example.org/problems/version-not-acceptable", + "title": "Version not acceptable", + "status": 406, + "detail": "The requested OEAPI version '7.0' cannot be served.", + "requestedVersion": "7.0", + "supportedVersions": ["5.0", "6.1"], + "instance": "https://api.example.org/courses" } ``` The requested major version is not supported. Fallback is not permitted and the request is rejected. + +--- + +### Example 4: unsupported consumer version + +Client: + +```http +POST /enrolments +Accept: application/vnd.oeapi+json;version=6.1;consumer=mbo-oke-roster-service;consumer-version=7.0 +``` + +Server: + +```http +HTTP/1.1 406 Not Acceptable +``` + +Response body: + +```json +{ + "type": "https://api.example.org/problems/version-not-acceptable", + "title": "Version not acceptable", + "status": 406, + "detail": "The requested consumer version '7.0' cannot be served.", + "consumer": { + "consumerKey": "mbo-oke-roster-service" + }, + "requestedVersion": "7.0", + "supportedVersions": ["0.95", "1.0", "6.1"], + "instance": "https://api.example.org/courses" +} +``` + +The requested consumer version is not supported. +Fallback is not permitted and the request is rejected.