diff --git a/code/API_definitions/device-roaming-status-subscriptions.yaml b/code/API_definitions/device-roaming-status-subscriptions.yaml index 8c9312a..dd9c08a 100644 --- a/code/API_definitions/device-roaming-status-subscriptions.yaml +++ b/code/API_definitions/device-roaming-status-subscriptions.yaml @@ -143,7 +143,7 @@ info: The list of error codes in this API specification is not exhaustive. Therefore the API specification MAY not document some non-mandatory error statuses as indicated in `CAMARA API Design Guide`. - Please refer to the `CAMARA_common.yaml` of the Commonalities Release associated to this API version for a complete list of error responses. The applicable Commonalities Release can be identified in the `API Readiness Checklist` document associated to this API version. + Please refer to the `CAMARA_common.yaml` of the Commonalities Release associated to this API version for a complete list of error responses. The applicable Commonalities Release can be identified from the `x-camara-commonalities` field, the changelog and the metadata of the released API version. As a specific rule, error `501 - NOT_IMPLEMENTED` can be only a possible error response if it is explicitly documented in the API. @@ -239,15 +239,15 @@ paths: x-correlator: $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" "400": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic400" + $ref: "../common/CAMARA_common.yaml#/components/responses/BadRequest400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" "410": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic410" + $ref: "../common/CAMARA_event_common.yaml#/components/responses/SinkGone410" "429": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic429" + $ref: "#/components/responses/TooManyRequestsOrQuotaExceeded429" security: - {} - notificationsBearerAuth: [] @@ -279,15 +279,15 @@ paths: "400": $ref: "../common/CAMARA_event_common.yaml#/components/responses/CreateSubscriptionBadRequest400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": $ref: "../common/CAMARA_event_common.yaml#/components/responses/SubscriptionPermissionDenied403" "409": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic409" + $ref: "../common/CAMARA_event_common.yaml#/components/responses/CreateSubscriptionConflict409" "422": $ref: "../common/CAMARA_event_common.yaml#/components/responses/CreateSubscriptionUnprocessableEntity422" "429": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic429" + $ref: "#/components/responses/TooManyRequestsOrQuotaExceeded429" get: tags: @@ -324,11 +324,11 @@ paths: empty-list-of-subscriptions: $ref: "#/components/examples/EMPTY_SUBSCRIPTION_LIST" "400": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic400" + $ref: "../common/CAMARA_common.yaml#/components/responses/BadRequest400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" /subscriptions/{subscriptionId}: get: @@ -365,11 +365,11 @@ paths: "400": $ref: "../common/CAMARA_event_common.yaml#/components/responses/SubscriptionIdRequired400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" "404": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic404" + $ref: "../common/CAMARA_common.yaml#/components/responses/NotFound404" delete: tags: @@ -401,11 +401,11 @@ paths: "400": $ref: "../common/CAMARA_event_common.yaml#/components/responses/SubscriptionIdRequired400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" "404": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic404" + $ref: "../common/CAMARA_common.yaml#/components/responses/NotFound404" components: securitySchemes: @@ -423,6 +423,32 @@ components: schema: $ref: "../common/CAMARA_event_common.yaml#/components/schemas/SubscriptionId" + responses: + TooManyRequestsOrQuotaExceeded429: + description: Too Many Requests or Quota Exceeded + 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: + - 429 + code: + enum: + - TOO_MANY_REQUESTS + - QUOTA_EXCEEDED + examples: + GENERIC_429_TOO_MANY_REQUESTS: + $ref: "../common/CAMARA_common.yaml#/components/examples/GENERIC_429_TOO_MANY_REQUESTS" + GENERIC_429_QUOTA_EXCEEDED: + $ref: "../common/CAMARA_common.yaml#/components/examples/GENERIC_429_QUOTA_EXCEEDED" + schemas: CreateSubscriptionDetail: description: The detail of the requested event subscription. @@ -503,7 +529,7 @@ components: protocol: $ref: "../common/CAMARA_event_common.yaml#/components/schemas/Protocol" sink: - $ref: "#/components/schemas/Sink" + $ref: "../common/CAMARA_event_common.yaml#/components/schemas/Sink" sinkCredential: $ref: "../common/CAMARA_event_common.yaml#/components/schemas/SinkCredential" types: @@ -578,7 +604,7 @@ components: protocol: $ref: "../common/CAMARA_event_common.yaml#/components/schemas/Protocol" sink: - $ref: "#/components/schemas/Sink" + $ref: "../common/CAMARA_event_common.yaml#/components/schemas/Sink" sinkCredential: $ref: "../common/CAMARA_event_common.yaml#/components/schemas/SinkCredential" types: @@ -797,14 +823,6 @@ components: $ref: "../common/CAMARA_common.yaml#/components/schemas/DeviceResponse" - $ref: "../common/CAMARA_event_common.yaml#/components/schemas/SubscriptionEnded" - Sink: - description: The address to which events shall be delivered using the selected protocol. - type: string - format: uri - maxLength: 2048 - pattern: ^https:\/\/.+$ - example: "https://endpoint.example.com/sink" - HTTPSubscriptionRequest: description: Subscription request for HTTP-based event delivery. allOf: diff --git a/code/API_definitions/device-roaming-status.yaml b/code/API_definitions/device-roaming-status.yaml index ac720c2..03841b7 100644 --- a/code/API_definitions/device-roaming-status.yaml +++ b/code/API_definitions/device-roaming-status.yaml @@ -85,7 +85,7 @@ info: The list of error codes in this API specification is not exhaustive. Therefore the API specification MAY not document some non-mandatory error statuses as indicated in `CAMARA API Design Guide`. - Please refer to the `CAMARA_common.yaml` of the Commonalities Release associated to this API version for a complete list of error responses. The applicable Commonalities Release can be identified in the `API Readiness Checklist` document associated to this API version. + Please refer to the `CAMARA_common.yaml` of the Commonalities Release associated to this API version for a complete list of error responses. The applicable Commonalities Release can be identified from the `x-camara-commonalities` field, the changelog and the metadata of the released API version. As a specific rule, error `501 - NOT_IMPLEMENTED` can be only a possible error response if it is explicitly documented in the API. @@ -205,25 +205,73 @@ paths: countryCode: 340 countryName: ["BL", "GF", "GP", "MF", "MQ"] "400": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic400" + $ref: "../common/CAMARA_common.yaml#/components/responses/BadRequestWithRange400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic401" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic403" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" "404": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic404" + $ref: "../common/CAMARA_common.yaml#/components/responses/IdentifierNotFound404" "422": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic422" + $ref: "../common/CAMARA_common.yaml#/components/responses/DeviceIdentifier422" "429": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic429" + $ref: "#/components/responses/TooManyRequestsOrQuotaExceeded429" "503": - $ref: "../common/CAMARA_common.yaml#/components/responses/Generic503" + $ref: "#/components/responses/ServiceUnavailable503" components: securitySchemes: openId: $ref: "../common/CAMARA_common.yaml#/components/securitySchemes/openId" + responses: + TooManyRequestsOrQuotaExceeded429: + description: Too Many Requests or Quota Exceeded + 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: + - 429 + code: + enum: + - TOO_MANY_REQUESTS + - QUOTA_EXCEEDED + examples: + GENERIC_429_TOO_MANY_REQUESTS: + $ref: "../common/CAMARA_common.yaml#/components/examples/GENERIC_429_TOO_MANY_REQUESTS" + GENERIC_429_QUOTA_EXCEEDED: + $ref: "../common/CAMARA_common.yaml#/components/examples/GENERIC_429_QUOTA_EXCEEDED" + + ServiceUnavailable503: + description: Service Unavailable + 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: + - 503 + code: + enum: + - UNAVAILABLE + examples: + GENERIC_503_UNAVAILABLE: + $ref: "../common/CAMARA_common.yaml#/components/examples/GENERIC_503_UNAVAILABLE" + schemas: LastStatusTime: description: | diff --git a/code/Test_definitions/device-roaming-status-subscriptions-createDeviceRoamingStatusSubscription.feature b/code/Test_definitions/device-roaming-status-subscriptions-createDeviceRoamingStatusSubscription.feature index c5cc116..dde875e 100644 --- a/code/Test_definitions/device-roaming-status-subscriptions-createDeviceRoamingStatusSubscription.feature +++ b/code/Test_definitions/device-roaming-status-subscriptions-createDeviceRoamingStatusSubscription.feature @@ -173,6 +173,63 @@ Feature: Device Roaming Status Subscriptions API, vwip - Operation createDeviceR And the notification property "$.data.subscriptionId" is equal to "id" And the notification request property "$.data.terminationReason" is equal to "MAX_EVENTS_REACHED" + @roaming_status_subscriptions_09_subscription_creation_initial_event + Scenario: Receive initial event notification on creation + Given the API supports initial events to be sent + And a valid subscription request body with property "$.config.initialEvent" set to true + When the request "createDeviceRoamingStatusSubscription" is sent + Then the response code is 201 or 202 + And an event notification of the subscribed type is received on callback-url + And notification body complies with the OAS schema at "#/components/schemas/CloudEvent" + + @roaming_status_subscriptions_10_Create_roaming_status_subscription_sync_with_accesstoken_sink_credential + Scenario: Create roaming status subscription (sync creation) with ACCESSTOKEN sinkCredential + # Some implementations may only support asynchronous subscription creation + # Some implementations may decide to not return the sinkCredential in the response (data minimization principle) + Given that subscriptions are created synchronously + And a valid subscription request body + And the request property "$.sinkCredential.credentialType" is set to "ACCESSTOKEN" + And the request property "$.sinkCredential.accessTokenType" is set to "bearer" + And the request property "$.sinkCredential.accessToken" is set to a valid access token + And the request property "$.sinkCredential.accessTokenExpiresUtc" is set to a valid expiry date in the future + When the request "createDeviceRoamingStatusSubscription" is sent + Then the response code is 201 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.sinkCredential.credentialType", if present, is set to value "ACCESSTOKEN" + And the response body property "$.sinkCredential.accessTokenExpiresUtc", if present, is set to the same value of the request property "$.sinkCredential.accessTokenExpiresUtc" + + @roaming_status_subscriptions_11_Create_roaming_status_subscription_sync_with_private_jwt_key_sink_credential_out_of_band_provisioning + Scenario: Create roaming status subscription (sync creation) with PRIVATE_JWT_KEY sinkCredential, out-of-band provisioning + # Some implementations may only support asynchronous subscription creation + # Some implementations may only support out_of_band provisioning + Given that subscriptions are created synchronously + And a valid subscription request body + And the request property "$.sinkCredential.credentialType" is set to "PRIVATE_JWT_KEY" + When the request "createDeviceRoamingStatusSubscription" is sent + Then the response code is 201 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + + @roaming_status_subscriptions_12_Create_roaming_status_subscription_sync_with_private_jwt_key_sink_credential_in_band_provisioning + Scenario: Create roaming status subscription (sync creation) with PRIVATE_JWT_KEY sinkCredential, in-band provisioning + # Some implementations may only support asynchronous subscription creation + # Some implementations may additionally support in_band provisioning + Given that subscriptions are created synchronously + And a valid subscription request body + And the request property "$.sinkCredential.credentialType" is set to "PRIVATE_JWT_KEY" + And the request property "$.sinkCredential.clientId" is set to a valid value + And the request property "$.sinkCredential.tokenUri" is set to a valid value + When the request "createDeviceRoamingStatusSubscription" is sent + Then the response code is 201 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.sinkCredential.credentialType" is set to value "PRIVATE_JWT_KEY" + And the response body property "$.sinkCredential.jwksUri" is set to a valid value + ################ # Error scenarios for management of input parameter device ################## @@ -199,10 +256,10 @@ Feature: Device Roaming Status Subscriptions API, vwip - Operation createDeviceR Examples: | device_identifier | oas_spec_schema | - | $.device.phoneNumber | /components/schemas/PhoneNumber | - | $.device.ipv4Address | /components/schemas/DeviceIpv4Addr | - | $.device.ipv6Address | /components/schemas/DeviceIpv6Address | - | $.device.networkIdentifier | /components/schemas/NetworkAccessIdentifier | + | $.device.phoneNumber | #/components/schemas/PhoneNumber | + | $.device.ipv4Address | #/components/schemas/DeviceIpv4Addr | + | $.device.ipv6Address | #/components/schemas/DeviceIpv6Address | + | $.device.networkIdentifier | #/components/schemas/NetworkAccessIdentifier | # This scenario may happen e.g. with 2-legged access tokens, which do not identify a single device. @roaming_status_subscriptions_C01.03_device_not_found @@ -446,3 +503,14 @@ Feature: Device Roaming Status Subscriptions API, vwip - Operation createDeviceR And the response property "$.status" is 422 And the response property "$.code" is "MULTIEVENT_SUBSCRIPTION_NOT_SUPPORTED" And the response property "$.message" contains a user friendly text + + @roaming_status_subscriptions_422.02_creation_with_private_jwt_key_not_configured + Scenario: Private JWT Key not configured for subscription creation + Given the API provider requires the use of a Private JWT key mechanism for subscription creation authentication + And the Private JWT key mechanism is not pre-configured in the environment + And a valid subscription request body with the property "$.sinkCredential.credentialType" set to "PRIVATE_KEY_JWT" + When the request "createDeviceRoamingStatusSubscription" is sent + Then the response code is 422 + And the response property "$.status" is 422 + And the response property "$.code" is "PRIVATE_KEY_JWT_NOT_CONFIGURED" + And the response property "$.message" contains a user friendly text diff --git a/code/Test_definitions/device-roaming-status-subscriptions-retrieveDeviceRoamingStatusSubscription.feature b/code/Test_definitions/device-roaming-status-subscriptions-retrieveDeviceRoamingStatusSubscription.feature index fecf088..839bc48 100644 --- a/code/Test_definitions/device-roaming-status-subscriptions-retrieveDeviceRoamingStatusSubscription.feature +++ b/code/Test_definitions/device-roaming-status-subscriptions-retrieveDeviceRoamingStatusSubscription.feature @@ -48,6 +48,31 @@ Feature: Device Roaming Status Subscriptions API, vwip - Operation retrieveDevic And the response property "$.id" is equal to "id" And the response property "$.config.subscriptionDetail.device" is not present + @roaming_status_subscriptions_03_Operation_to_retrieve_subscription_based_on_an_existing_subscription-id_access_token_sink_credential_returned + # Some implementations may decide to not return the sinkCredential in the response (data minimization principle) + Scenario: Get a subscription based on existing subscription-id, with ACCESSTOKEN sinkCredential returned. + Given the path parameter "subscriptionId" is set to the identifier of an existing roaming status subscription + When the request "retrieveDeviceRoamingStatusSubscription" is sent + Then the response code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.sinkCredential.credentialType", if present, is set to value "ACCESSTOKEN" + And the response body property "$.sinkCredential.accessTokenExpiresUtc", if present, is set to the same value of the request property "$.sinkCredential.accessTokenExpiresUtc" + + @roaming_status_subscriptions_04_Operation_to_retrieve_subscription_based_on_an_existing_subscription-id_private_jwt_key_sink_credential_returned + # Some implementations may decide to not return the sinkCredential in the response (data minimization principle) + # Mainly applicable for in-band provisioning of PRIVATE_JWT_KEY mode for a given subscription + Scenario: Get a subscription based on existing subscription-id, with PRIVATE_JWT_KEY sinkCredential returned. + Given the path parameter "subscriptionId" is set to the identifier of an existing roaming status subscription + When the request "retrieveDeviceRoamingStatusSubscription" is sent + Then the response code is 200 + And the response header "Content-Type" is "application/json" + And the response header "x-correlator" has the same value as the request header "x-correlator" + And the response body complies with the OAS schema at "#/components/schemas/Subscription" + And the response body property "$.sinkCredential.credentialType" is set to value "PRIVATE_JWT_KEY" + And the response body property "$.sinkCredential.jwksUri" is set to a valid value + ################ # Error scenarios for management of input parameter device ################## diff --git a/code/Test_definitions/device-roaming-status.feature b/code/Test_definitions/device-roaming-status.feature index e95ffdf..dff737f 100644 --- a/code/Test_definitions/device-roaming-status.feature +++ b/code/Test_definitions/device-roaming-status.feature @@ -78,10 +78,10 @@ Feature: CAMARA Device Roaming Status API, vwip - Operation getRoamingStatus Examples: | device_identifier | oas_spec_schema | - | $.device.phoneNumber | /components/schemas/PhoneNumber | - | $.device.ipv4Address | /components/schemas/DeviceIpv4Addr | - | $.device.ipv6Address | /components/schemas/DeviceIpv6Address | - | $.device.networkIdentifier | /components/schemas/NetworkAccessIdentifier | + | $.device.phoneNumber | #/components/schemas/PhoneNumber | + | $.device.ipv4Address | #/components/schemas/DeviceIpv4Addr | + | $.device.ipv6Address | #/components/schemas/DeviceIpv6Address | + | $.device.networkIdentifier | #/components/schemas/NetworkAccessIdentifier | # This scenario may happen e.g. with 2-legged access tokens, which do not identify a single device. @device_roaming_status_C01.03_device_not_found @@ -140,7 +140,7 @@ Feature: CAMARA Device Roaming Status API, vwip - Operation getRoamingStatus # Error code 401 ################# - @device_roaming_status_401.1_expired_access_token + @device_roaming_status_401.01_expired_access_token Scenario: Expired access token Given the header "Authorization" is set to an expired access token And the request body is set to a valid request body @@ -150,7 +150,7 @@ Feature: CAMARA Device Roaming Status API, vwip - Operation getRoamingStatus And the response property "$.code" is "UNAUTHENTICATED" And the response property "$.message" contains a user friendly text - @device_roaming_status_401.2_no_authorization_header + @device_roaming_status_401.02_no_authorization_header Scenario: No Authorization header Given the header "Authorization" is removed And the request body is set to a valid request body @@ -160,7 +160,7 @@ Feature: CAMARA Device Roaming Status API, vwip - Operation getRoamingStatus And the response property "$.code" is "UNAUTHENTICATED" And the response property "$.message" contains a user friendly text - @device_roaming_status_401.3_malformed_access_token + @device_roaming_status_401.03_malformed_access_token Scenario: Malformed access token Given the header "Authorization" is set to a malformed token And the request body is set to a valid request body @@ -175,7 +175,7 @@ Feature: CAMARA Device Roaming Status API, vwip - Operation getRoamingStatus # Error code 403 ################# - @device_roaming_status_403_permission_denied + @device_roaming_status_403.01_permission_denied Scenario: OAuth2 token access does not have the required scope Given header "Authorization" set to an access token not including scope "device-roaming-status:read" And the request body is set to a valid request body @@ -185,11 +185,39 @@ Feature: CAMARA Device Roaming Status API, vwip - Operation getRoamingStatus And the response property "$.code" is "PERMISSION_DENIED" And the response property "$.message" contains a user friendly text +################# +# Error code 429 +################# + + @device_roaming_status_429.01_Too_Many_Requests +  #To test this scenario environment has to be configured to reject requests reaching the threshold limit set. + Scenario: Request is rejected due to threshold policy + Given a valid request for "getRoamingStatus" + And the header "Authorization" is set to a valid access token + And the threshold of requests has been reached + When the request "getRoamingStatus" is sent + Then the response status code is 429 + And the response property "$.status" is 429 + And the response property "$.code" is "TOO_MANY_REQUESTS" + And the response property "$.message" contains a user friendly text + + @device_roaming_status_429.02_Quota_Exceeded +  #To test this scenario environment has to be configured to reject requests reaching the allocated quota. + Scenario: Request is rejected due to API consumer quota being reached + Given a valid request for "getRoamingStatus" + And the header "Authorization" is set to a valid access token + And the API consumer allocated quota of requests has been reached + When the request "getRoamingStatus" is sent + Then the response status code is 429 + And the response property "$.status" is 429 + And the response property "$.code" is "QUOTA_EXCEEDED" + And the response property "$.message" contains a user friendly text + ################# # Error code 503 ################# - @device_roaming_status_503_network_error + @device_roaming_status_503.01_network_error Scenario: Network error temporarily prevents the device roaming status from being retrieved # This test is for use by the API provider only Given a valid testing device supported by the service, identified by the token or provided in the request body