diff --git a/README.md b/README.md index 59942820..0d296649 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,11 @@ The documents that are relevant for CAMARA API Repositories are found in the `do The `artifacts` directory contains: * templates for creating Github issues -* common data and error formats for CAMARA APIs in [CAMARA_common.yaml](artifacts/CAMARA_common.yaml) +* common data and error formats for CAMARA APIs in [CAMARA_common.yaml](artifacts/common/CAMARA_common.yaml) +* API templates demonstrating `$ref` consumption of common schemas: in [artifacts/api-templates](artifacts/api-templates) folder * notification subscription template: [event-subscription-template.yaml](artifacts/camara-cloudevents/event-subscription-template.yaml) * OAS definition of CAMARA Event using CloudEvents: [notification-as-cloud-event.yaml](artifacts/notification-as-cloud-event.yaml) -* Common artifacts for testing error scenarios for device and phoneNumber: in [artifacts/testing](artifacts/testing) folder +* Common artifacts for testing error scenarios for device and phoneNumber: in [artifacts/testing](artifacts/testing) folder ### Frequently-accessed output documents diff --git a/artifacts/api-templates/sample-service.yaml b/artifacts/api-templates/sample-service.yaml new file mode 100644 index 00000000..9989ad77 --- /dev/null +++ b/artifacts/api-templates/sample-service.yaml @@ -0,0 +1,232 @@ +openapi: 3.0.3 +info: + title: Sample Service + description: | + Template for a request-response CAMARA API. + + Copy this file to your API repository's `code/API_definitions/` directory + and adapt it to your API's resources and operations. + + This template demonstrates: + - `$ref` to `../common/CAMARA_common.yaml` for shared schemas, error responses, + parameters, headers, and security schemes + - Two options for error responses: + - Option A: Pure `$ref` for responses using only generic error codes + - Option B: Local response definition extending generic errors with API-specific codes + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + version: wip + x-camara-commonalities: 0.7.0 +servers: + - url: "{apiRoot}/sample-service/vwip" + variables: + apiRoot: + default: http://localhost:9091 + description: API root, Apache 2.0 licensed +tags: + - name: Resources + description: Operations on sample resources +security: + - openId: + - sample-service:resource:read + - sample-service:resource:write +paths: + /resources: + post: + tags: + - Resources + summary: Create a resource + description: Creates a new resource with the provided properties. + operationId: createResource + parameters: + - $ref: "../common/CAMARA_common.yaml#/components/parameters/x-correlator" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateResource" + responses: + "201": + description: Resource created + headers: + x-correlator: + $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" + content: + application/json: + schema: + $ref: "#/components/schemas/Resource" + "400": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic400" + "401": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + "403": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + "409": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic409" + "422": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic422" + "429": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic429" + "500": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic500" + get: + tags: + - Resources + summary: List resources + description: Returns a list of all resources accessible to the API consumer. + operationId: listResources + parameters: + - $ref: "../common/CAMARA_common.yaml#/components/parameters/x-correlator" + responses: + "200": + description: List of resources + headers: + x-correlator: + $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Resource" + "401": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + "403": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + "500": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic500" + /resources/{resourceId}: + get: + tags: + - Resources + summary: Get a resource by ID + description: Returns the details of a specific resource identified by its ID. + operationId: getResource + parameters: + - $ref: "#/components/parameters/ResourceId" + - $ref: "../common/CAMARA_common.yaml#/components/parameters/x-correlator" + responses: + "200": + description: Resource details + headers: + x-correlator: + $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" + content: + application/json: + schema: + $ref: "#/components/schemas/Resource" + "401": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + "403": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + # Option A — pure $ref for responses using only generic error codes + "404": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic404" + "500": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic500" + delete: + tags: + - Resources + summary: Delete a resource + description: Deletes a specific resource identified by its ID. + operationId: deleteResource + parameters: + - $ref: "#/components/parameters/ResourceId" + - $ref: "../common/CAMARA_common.yaml#/components/parameters/x-correlator" + responses: + "204": + description: Resource deleted + headers: + x-correlator: + $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" + "401": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + "403": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + # Option B — local response extending generic errors with API-specific codes + "404": + $ref: "#/components/responses/ResourceNotFound404" + "500": + $ref: "../common/CAMARA_common.yaml#/components/responses/Generic500" +components: + securitySchemes: + openId: + $ref: "../common/CAMARA_common.yaml#/components/securitySchemes/openId" + responses: + # Option B example: API-specific error response extending generic error codes. + # Use this pattern when an operation needs API-specific error codes in addition + # to the generic ones defined in CAMARA_common.yaml. + ResourceNotFound404: + description: Resource not found + headers: + x-correlator: + $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" + content: + application/json: + schema: + allOf: + - $ref: "../common/CAMARA_common.yaml#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 404 + code: + enum: + - NOT_FOUND + - IDENTIFIER_NOT_FOUND + examples: + GENERIC_404_NOT_FOUND: + description: The specified resource was not found + value: + status: 404 + code: NOT_FOUND + message: The specified resource is not found. + RESOURCE_404_IDENTIFIER_NOT_FOUND: + description: API-specific — some identifier cannot be matched to a resource + value: + status: 404 + code: IDENTIFIER_NOT_FOUND + message: Some of the provided identifiers are not found. + parameters: + ResourceId: + name: resourceId + in: path + required: true + description: Identifier of the resource to operate on + schema: + $ref: "#/components/schemas/ResourceId" + schemas: + ResourceId: + type: string + format: uuid + description: Unique identifier for the resource + CreateResource: + description: Properties for creating a new resource + type: object + required: + - name + properties: + name: + type: string + maxLength: 128 + description: Human-readable name for the resource + device: + $ref: "../common/CAMARA_common.yaml#/components/schemas/Device" + Resource: + description: A resource managed by the API + type: object + required: + - resourceId + - name + properties: + resourceId: + $ref: "#/components/schemas/ResourceId" + name: + type: string + maxLength: 128 + description: Human-readable name for the resource + device: + $ref: "../common/CAMARA_common.yaml#/components/schemas/Device" diff --git a/artifacts/camara-cloudevents/event-subscription-template.yaml b/artifacts/camara-cloudevents/event-subscription-template.yaml index 17b28d80..c42a0598 100644 --- a/artifacts/camara-cloudevents/event-subscription-template.yaml +++ b/artifacts/camara-cloudevents/event-subscription-template.yaml @@ -13,7 +13,7 @@ info: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html version: 0.4.0-rc.1 - x-camara-commonalities: "0.7" + x-camara-commonalities: 0.7.0 externalDocs: description: Product documentation at CAMARA diff --git a/artifacts/CAMARA_common.yaml b/artifacts/common/CAMARA_common.yaml similarity index 90% rename from artifacts/CAMARA_common.yaml rename to artifacts/common/CAMARA_common.yaml index a3ec59f9..2bf87fd1 100644 --- a/artifacts/CAMARA_common.yaml +++ b/artifacts/common/CAMARA_common.yaml @@ -7,7 +7,7 @@ info: url: https://www.apache.org/licenses/LICENSE-2.0.html version: 0.8.0-rc.1 - x-camara-commonalities: "0.7" + x-camara-commonalities: 0.7.0 paths: {} components: @@ -309,7 +309,7 @@ components: enum: - INVALID_ARGUMENT - OUT_OF_RANGE - - "{{SPECIFIC_CODE}}" + # - "{{SPECIFIC_CODE}}" - API-specific codes added if needed examples: GENERIC_400_INVALID_ARGUMENT: description: Invalid Argument. Generic Syntax Exception @@ -323,12 +323,12 @@ components: status: 400 code: OUT_OF_RANGE message: Client specified an invalid range. - GENERIC_400_{{SPECIFIC_CODE}}: - description: Specific Syntax Exception regarding a field that is relevant in the context of the API - value: - status: 400 - code: "{{SPECIFIC_CODE}}" - message: Message for specific code + # GENERIC_400_{{SPECIFIC_CODE}}: + # description: Specific Syntax Exception regarding a field that is relevant in the context of the API + # value: + # status: 400 + # code: "{{SPECIFIC_CODE}}" + # message: Message for specific code Generic401: description: Unauthorized headers: @@ -373,7 +373,7 @@ components: enum: - PERMISSION_DENIED - INVALID_TOKEN_CONTEXT - - "{{SPECIFIC_CODE}}" + # - "{{SPECIFIC_CODE}}" - API-specific codes added if needed examples: GENERIC_403_PERMISSION_DENIED: description: Permission denied. OAuth2 token access does not have the required scope or when the user fails operational security @@ -386,13 +386,14 @@ components: value: status: 403 code: INVALID_TOKEN_CONTEXT - message: "{{field}} is not consistent with access token." - GENERIC_403_{{SPECIFIC_CODE}}: - description: Indicate a Business Logic condition that forbids a process not attached to a specific field in the context of the API - value: - status: 403 - code: "{{SPECIFIC_CODE}}" - message: Message for specific code + # message: "{{field}} is not consistent with access token." + message: "... is not consistent with access token." + # GENERIC_403_{{SPECIFIC_CODE}}: + # description: Indicate a Business Logic condition that forbids a process not attached to a specific field in the context of the API + # value: + # status: 403 + # code: "{{SPECIFIC_CODE}}" + # message: Message for specific code Generic404: description: Not found headers: @@ -412,7 +413,7 @@ components: enum: - NOT_FOUND - IDENTIFIER_NOT_FOUND - - "{{SPECIFIC_CODE}}" + # - "{{SPECIFIC_CODE}}" - API-specific codes added if needed examples: GENERIC_404_NOT_FOUND: description: Resource is not found @@ -426,12 +427,12 @@ components: status: 404 code: IDENTIFIER_NOT_FOUND message: Device identifier not found. - GENERIC_404_{{SPECIFIC_CODE}}: - description: Specific situation to highlight the resource/concept not found - value: - status: 404 - code: "{{SPECIFIC_CODE}}" - message: Message for specific code + # GENERIC_404_{{SPECIFIC_CODE}}: + # description: Specific situation to highlight the resource/concept not found + # value: + # status: 404 + # code: "{{SPECIFIC_CODE}}" + # message: Message for specific code Generic405: description: Method Not Allowed headers: @@ -503,7 +504,7 @@ components: - ALREADY_EXISTS - CONFLICT - INCOMPATIBLE_STATE - - "{{SPECIFIC_CODE}}" + # - "{{SPECIFIC_CODE}}" - API-specific codes added if needed examples: GENERIC_409_ABORTED: description: The resource is undergoing modification by another process @@ -521,7 +522,7 @@ components: ################################### # This Error Code is DEPRECATED ################################### - description: Duplication of an existing resource + description: Duplication of an existing resource value: status: 409 code: CONFLICT @@ -533,13 +534,12 @@ components: status: 409 code: INCOMPATIBLE_STATE message: Resource must be in AVAILABLE state to extend. Current state is UNAVAILABLE. - - GENERIC_409_{{SPECIFIC_CODE}}: - description: Specific conflict situation that is relevant in the context of the API - value: - status: 409 - code: "{{SPECIFIC_CODE}}" - message: Message for specific code + # GENERIC_409_{{SPECIFIC_CODE}}: + # description: Specific conflict situation that is relevant in the context of the API + # value: + # status: 409 + # code: "{{SPECIFIC_CODE}}" + # message: Message for specific code Generic410: description: Gone headers: @@ -636,7 +636,7 @@ components: - MISSING_IDENTIFIER - UNSUPPORTED_IDENTIFIER - UNNECESSARY_IDENTIFIER - - "{{SPECIFIC_CODE}}" + # - "{{SPECIFIC_CODE}}" - API-specific codes added if needed examples: GENERIC_422_SERVICE_NOT_APPLICABLE: description: Service not applicable for the provided identifier @@ -662,12 +662,12 @@ components: status: 422 code: UNNECESSARY_IDENTIFIER message: The device is already identified by the access token. - GENERIC_422_{{SPECIFIC_CODE}}: - description: Any semantic condition associated to business logic, specifically related to a field or data structure - value: - status: 422 - code: "{{SPECIFIC_CODE}}" - message: Message for specific code + # GENERIC_422_{{SPECIFIC_CODE}}: + # description: Any semantic condition associated to business logic, specifically related to a field or data structure + # value: + # status: 422 + # code: "{{SPECIFIC_CODE}}" + # message: Message for specific code Generic429: description: Too Many Requests headers: diff --git a/documentation/CAMARA-API-Design-Guide.md b/documentation/CAMARA-API-Design-Guide.md index f22c2c1b..3db28041 100644 --- a/documentation/CAMARA-API-Design-Guide.md +++ b/documentation/CAMARA-API-Design-Guide.md @@ -87,7 +87,7 @@ that this point is open to continuous evolution over time through the addition o To allow for proper management of this ever-evolving list, an external repository has been defined to that end. This repository is referenced below. -[Link to Common Data Types documentation repository](../artifacts/CAMARA_common.yaml) +[Link to Common Data Types documentation repository](../artifacts/common/CAMARA_common.yaml) ### 2.2. Data Definitions @@ -370,7 +370,7 @@ An error representation MUST NOT differ from the representation of any resource. All these aforementioned fields are mandatory in Error Responses. `status` and `code` fields have normative nature, so as their use has to be standardized (see [3.2.1. Standardized use of CAMARA error responses](#321-standardized-use-of-camara-error-responses)). On the other hand, `message` is informative and within this document an example is shown. -The values of the `status` and `code` fields are normative (i.e. they have a set of allowed values), as defined in [CAMARA_common.yaml](../artifacts/CAMARA_common.yaml). +The values of the `status` and `code` fields are normative (i.e. they have a set of allowed values), as defined in [CAMARA_common.yaml](../artifacts/common/CAMARA_common.yaml). An example of JSON error structure is as follows: @@ -487,7 +487,7 @@ The Following table compiles the guidelines to be adopted: | 5 | An identifier is not included in the request and the device or phone number identification cannot be derived from the 3-legged access token | 422 | MISSING_IDENTIFIER | The device cannot be identified. | **NOTE:** -The `Device` object defined in [CAMARA_common.yaml](/artifacts/CAMARA_common.yaml) allows the API consumer to provide more than one device identifier. This is to allow the API consumer to provide additional information to a given API provider that might be useful for their implementation of the API, or to different API providers who might prefer different identifier types, or might not support all possible device identifiers. +The `Device` object defined in [CAMARA_common.yaml](/artifacts/common/CAMARA_common.yaml) allows the API consumer to provide more than one device identifier. This is to allow the API consumer to provide additional information to a given API provider that might be useful for their implementation of the API, or to different API providers who might prefer different identifier types, or might not support all possible device identifiers. Where an API consumer provides more than one device identifier, it is RECOMMENDED that the API provider include in the response a single device identifier (from those provided) which they are using to fulfil the API. This would apply even if the device identifiers do not all match the same device, as the API provider does not perform any logic to validate/correlate that the indicated device identifiers match the same device. @@ -764,8 +764,8 @@ info: license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html - # CAMARA Commonalities minor version - x.y - x-camara-commonalities: "0.6" + # CAMARA Commonalities version + x-camara-commonalities: 0.7.0 ``` #### 5.3.1. Title @@ -801,7 +801,7 @@ license ``` #### 5.3.7. Extension Field -The API SHALL specify the Commonalities minor release number they are compliant to, by including the `x-camara-commonalities` extension field. +The API SHALL specify the Commonalities release version they are compliant to, by including the `x-camara-commonalities` extension field. The value is the full version string of the Commonalities release used by the API, as stored in `VERSION.yaml` at the corresponding Commonalities release tag (e.g. `0.7.0` for a public release, or `0.7.0-rc.1` for a pre-release). ### 5.4. ExternalDocs Object The `externalDocs` object SHALL have the following content: @@ -1398,7 +1398,7 @@ Make the information available: When an API requires a User (as defined by the [ICM Glossary](https://github.com/camaraproject/IdentityAndConsentManagement/blob/r3.3/documentation/CAMARA-API-access-and-user-consent.md#glossary-of-terms-and-concepts)) to be identified in order to get access to that User's data (as Resource Owner), the User can be identified in one of two ways: - If the access token is a Three-Legged Access Token, then the User will already have been associated with that token by the API provider, which in turn may be identified from the physical device that calls the `/authorize` endpoint for the OIDC authorisation code flow, or from the `login_hint` parameter of the OIDC CIBA flow (which can be a device IP, phone number or operator token). The `sub` claim of the ID token returned with the access token will confirm that an association with the User has been made, although this will not identify the User directly given that the `sub` will not be a globally unique identifier nor contain PII as per the [CAMARA Security and Interoperability Profile](https://github.com/camaraproject/IdentityAndConsentManagement/blob/r3.3/documentation/CAMARA-Security-Interoperability.md#id-token-sub-claim) requirements. -- If the access token is a Two-Legged Access Token, no User is associated with the token, and hence an explicit identifier MUST be provided. This is typically either a `Device` object named `device`, or a `PhoneNumber` string named `phoneNumber`. Both of these schema are defined in the [CAMARA_common.yaml](/artifacts/CAMARA_common.yaml) artifact. In both cases, it is the User that is being identified, although the `device` identifier allows this indirectly by identifying an active physical device. +- If the access token is a Two-Legged Access Token, no User is associated with the token, and hence an explicit identifier MUST be provided. This is typically either a `Device` object named `device`, or a `PhoneNumber` string named `phoneNumber`. Both of these schema are defined in the [CAMARA_common.yaml](/artifacts/common/CAMARA_common.yaml) artifact. In both cases, it is the User that is being identified, although the `device` identifier allows this indirectly by identifying an active physical device. If an API provider issues Three-Legged Access Tokens for use with the API, the following error may occur: - **Both a Three-Legged Access Token and an explicit User identifier (device or phone number) are provided by the API consumer.**