From 911cc4ae7f67ae696b8e2b13a1ddfed3e2b100cf Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Mon, 14 Sep 2026 17:17:25 +0100 Subject: [PATCH 01/13] Import common Sink schema from Commonalities r4.4 --- .../device-roaming-status-subscriptions.yaml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/code/API_definitions/device-roaming-status-subscriptions.yaml b/code/API_definitions/device-roaming-status-subscriptions.yaml index 8c9312a..6181334 100644 --- a/code/API_definitions/device-roaming-status-subscriptions.yaml +++ b/code/API_definitions/device-roaming-status-subscriptions.yaml @@ -503,7 +503,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 +797,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: From 2f5c6ab90ec9244d86a0ac8445131b852afef48c Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Mon, 14 Sep 2026 17:20:56 +0100 Subject: [PATCH 02/13] Change sink schema reference to common definitions --- code/API_definitions/device-roaming-status-subscriptions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/API_definitions/device-roaming-status-subscriptions.yaml b/code/API_definitions/device-roaming-status-subscriptions.yaml index 6181334..071a2e2 100644 --- a/code/API_definitions/device-roaming-status-subscriptions.yaml +++ b/code/API_definitions/device-roaming-status-subscriptions.yaml @@ -578,7 +578,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: From 4ccd6c0aadd1e5621c5b3e5afd3b257cb56125ac Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Fri, 25 Sep 2026 11:15:12 +0100 Subject: [PATCH 03/13] Update error responses to use either new common schemas or locally defined schemas --- .../device-roaming-status.yaml | 64 ++++++++++++++++--- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/code/API_definitions/device-roaming-status.yaml b/code/API_definitions/device-roaming-status.yaml index ac720c2..d152e7d 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/TooManyRequests429" "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: + TooManyRequests429: + description: Too Many Requests + 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: | From faf61b624212e06fa9a76ae19861b690d1f189ae Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Fri, 25 Sep 2026 11:34:23 +0100 Subject: [PATCH 04/13] Update error responses to use either new common schemas or locally defined schemas --- .../device-roaming-status-subscriptions.yaml | 62 +++++++++++++------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/code/API_definitions/device-roaming-status-subscriptions.yaml b/code/API_definitions/device-roaming-status-subscriptions.yaml index 071a2e2..326a748 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 + 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. From 39693e1abd00081ce0d4dcf40a0bc06ad8fae1a9 Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Fri, 25 Sep 2026 11:35:17 +0100 Subject: [PATCH 05/13] Rename TooManyRequests429 to TooManyRequestsOrQuotaExceeded429 --- code/API_definitions/device-roaming-status.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/API_definitions/device-roaming-status.yaml b/code/API_definitions/device-roaming-status.yaml index d152e7d..6bb3c76 100644 --- a/code/API_definitions/device-roaming-status.yaml +++ b/code/API_definitions/device-roaming-status.yaml @@ -215,7 +215,7 @@ paths: "422": $ref: "../common/CAMARA_common.yaml#/components/responses/DeviceIdentifier422‎" "429": - $ref: "#/components/responses/TooManyRequests429" + $ref: "#/components/responses/TooManyRequestsOrQuotaExceeded429" "503": $ref: "#/components/responses/ServiceUnavailable503" @@ -225,8 +225,8 @@ components: $ref: "../common/CAMARA_common.yaml#/components/securitySchemes/openId" responses: - TooManyRequests429: - description: Too Many Requests + TooManyRequestsOrQuotaExceeded429: + description: Too Many Requests or Quota Exceeded headers: x-correlator: $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" From af18d6e4c229a05f6de03fdc5a6e13549a751c57 Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Fri, 25 Sep 2026 11:36:02 +0100 Subject: [PATCH 06/13] Update TooManyRequestsOrQuotaExceeded429 description --- code/API_definitions/device-roaming-status-subscriptions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/API_definitions/device-roaming-status-subscriptions.yaml b/code/API_definitions/device-roaming-status-subscriptions.yaml index 326a748..4c29742 100644 --- a/code/API_definitions/device-roaming-status-subscriptions.yaml +++ b/code/API_definitions/device-roaming-status-subscriptions.yaml @@ -425,7 +425,7 @@ components: responses: TooManyRequestsOrQuotaExceeded429: - description: Too Many Requests + description: Too Many Requests or Quota Exceeded headers: x-correlator: $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" From d5b861f7166e6167746e79860951c9b64d8379da Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Fri, 25 Sep 2026 11:54:49 +0100 Subject: [PATCH 07/13] Remove hidden character from DeviceIdentifier422 reference --- code/API_definitions/device-roaming-status.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/API_definitions/device-roaming-status.yaml b/code/API_definitions/device-roaming-status.yaml index 6bb3c76..04717d3 100644 --- a/code/API_definitions/device-roaming-status.yaml +++ b/code/API_definitions/device-roaming-status.yaml @@ -213,7 +213,7 @@ paths: "404": $ref: "../common/CAMARA_common.yaml#/components/responses/IdentifierNotFound404" "422": - $ref: "../common/CAMARA_common.yaml#/components/responses/DeviceIdentifier422‎" + $ref: "../common/CAMARA_common.yaml#/components/responses/DeviceIdentifier422" "429": $ref: "#/components/responses/TooManyRequestsOrQuotaExceeded429" "503": From 43c4993ef997d5a24624988d88f984a8a0a59406 Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Fri, 25 Sep 2026 11:57:27 +0100 Subject: [PATCH 08/13] Remove hidden characters from references --- code/API_definitions/device-roaming-status.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/API_definitions/device-roaming-status.yaml b/code/API_definitions/device-roaming-status.yaml index 04717d3..03841b7 100644 --- a/code/API_definitions/device-roaming-status.yaml +++ b/code/API_definitions/device-roaming-status.yaml @@ -247,8 +247,8 @@ components: 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‎" + GENERIC_429_QUOTA_EXCEEDED: + $ref: "../common/CAMARA_common.yaml#/components/examples/GENERIC_429_QUOTA_EXCEEDED" ServiceUnavailable503: description: Service Unavailable @@ -270,7 +270,7 @@ components: - UNAVAILABLE examples: GENERIC_503_UNAVAILABLE: - $ref: "../common/CAMARA_common.yaml#/components/examples/GENERIC_503_UNAVAILABLE‎" + $ref: "../common/CAMARA_common.yaml#/components/examples/GENERIC_503_UNAVAILABLE" schemas: LastStatusTime: From b6431f083d47b1b9fec98cd5ee6e177942ea2ae0 Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Fri, 25 Sep 2026 12:00:42 +0100 Subject: [PATCH 09/13] Remove hidden characters from references --- .../device-roaming-status-subscriptions.yaml | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/code/API_definitions/device-roaming-status-subscriptions.yaml b/code/API_definitions/device-roaming-status-subscriptions.yaml index 4c29742..dd9c08a 100644 --- a/code/API_definitions/device-roaming-status-subscriptions.yaml +++ b/code/API_definitions/device-roaming-status-subscriptions.yaml @@ -239,15 +239,15 @@ paths: x-correlator: $ref: "../common/CAMARA_common.yaml#/components/headers/x-correlator" "400": - $ref: "../common/CAMARA_common.yaml#/components/responses/BadRequest400‎" + $ref: "../common/CAMARA_common.yaml#/components/responses/BadRequest400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401‎" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403‎" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" "410": - $ref: "../common/CAMARA_event_common.yaml#/components/responses/SinkGone410‎" + $ref: "../common/CAMARA_event_common.yaml#/components/responses/SinkGone410" "429": - $ref: "#/components/responses/TooManyRequestsOrQuotaExceeded429‎" + $ref: "#/components/responses/TooManyRequestsOrQuotaExceeded429" security: - {} - notificationsBearerAuth: [] @@ -279,11 +279,11 @@ paths: "400": $ref: "../common/CAMARA_event_common.yaml#/components/responses/CreateSubscriptionBadRequest400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401‎" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": $ref: "../common/CAMARA_event_common.yaml#/components/responses/SubscriptionPermissionDenied403" "409": - $ref: "../common/CAMARA_event_common.yaml#/components/responses/CreateSubscriptionConflict409‎" + $ref: "../common/CAMARA_event_common.yaml#/components/responses/CreateSubscriptionConflict409" "422": $ref: "../common/CAMARA_event_common.yaml#/components/responses/CreateSubscriptionUnprocessableEntity422" "429": @@ -324,11 +324,11 @@ paths: empty-list-of-subscriptions: $ref: "#/components/examples/EMPTY_SUBSCRIPTION_LIST" "400": - $ref: "../common/CAMARA_common.yaml#/components/responses/BadRequest400‎" + $ref: "../common/CAMARA_common.yaml#/components/responses/BadRequest400" "401": - $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401‎" + $ref: "../common/CAMARA_common.yaml#/components/responses/Unauthenticated401" "403": - $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403‎" + $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" /subscriptions/{subscriptionId}: get: @@ -369,7 +369,7 @@ paths: "403": $ref: "../common/CAMARA_common.yaml#/components/responses/PermissionDenied403" "404": - $ref: "../common/CAMARA_common.yaml#/components/responses/NotFound404‎" + $ref: "../common/CAMARA_common.yaml#/components/responses/NotFound404" delete: tags: @@ -446,8 +446,8 @@ components: 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‎" + GENERIC_429_QUOTA_EXCEEDED: + $ref: "../common/CAMARA_common.yaml#/components/examples/GENERIC_429_QUOTA_EXCEEDED" schemas: CreateSubscriptionDetail: From e052a2839bf60054666a6176746cbddd86585ea6 Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Fri, 25 Sep 2026 13:50:21 +0100 Subject: [PATCH 10/13] Add scenarios for device roaming status subscription creation --- ...ateDeviceRoamingStatusSubscription.feature | 76 ++++++++++++++++++- 1 file changed, 72 insertions(+), 4 deletions(-) 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 From 0f4f6654d3ebcdd5e9d31021c10d408a8ef9bf6e Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Fri, 25 Sep 2026 13:50:55 +0100 Subject: [PATCH 11/13] Update schema references to use correct format --- code/Test_definitions/device-roaming-status.feature | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/Test_definitions/device-roaming-status.feature b/code/Test_definitions/device-roaming-status.feature index e95ffdf..e71a905 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 From bcfdcccbe5e98f3f9aee9e1019e45e8db28830aa Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Fri, 25 Sep 2026 13:58:45 +0100 Subject: [PATCH 12/13] Add scenarios for retrieving subscriptions with credentials --- ...eveDeviceRoamingStatusSubscription.feature | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) 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 ################## From 9ec6213088da242bdc68251a7d8188480743072e Mon Sep 17 00:00:00 2001 From: Eric Murray Date: Fri, 25 Sep 2026 14:49:23 +0100 Subject: [PATCH 13/13] Add test scenarios for 429 errors --- .../device-roaming-status.feature | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/code/Test_definitions/device-roaming-status.feature b/code/Test_definitions/device-roaming-status.feature index e71a905..dff737f 100644 --- a/code/Test_definitions/device-roaming-status.feature +++ b/code/Test_definitions/device-roaming-status.feature @@ -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