diff --git a/CHANGELOG.md b/CHANGELOG.md index a2e67273b..b43835e86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 2025-03-10 +- [PI-387] Add Product Team Id to search results +- [PI-843] Add Read Product to test UI +- [PI-764] Refine Swagger + ## 2025-03-07 - [PI-383] External ID - [PI-838] product_team includes product_team_id diff --git a/VERSION b/VERSION index 82166197a..a7d6d5f25 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2025.03.07 +2025.03.10 diff --git a/changelog/2025-03-10.md b/changelog/2025-03-10.md new file mode 100644 index 000000000..48f663f64 --- /dev/null +++ b/changelog/2025-03-10.md @@ -0,0 +1,3 @@ +- [PI-387] Add Product Team Id to search results +- [PI-843] Add Read Product to test UI +- [PI-764] Refine Swagger diff --git a/infrastructure/swagger/02_info.yaml b/infrastructure/swagger/02_info.yaml index 974708506..260ff1c99 100644 --- a/infrastructure/swagger/02_info.yaml +++ b/infrastructure/swagger/02_info.yaml @@ -12,23 +12,32 @@ info: description: | ## Overview - API to configure internal systems to allow a Connecting Party to connect. A source of information about Connecting Parties (systems that connect to NHSE) that helps provide system identity attributes that ensure NHSE services know what systems they are connecting to and the legal entities they are sharing data with. This service is driven by APIs that enable self-service for activities such as certificate management and environment access. It also is a store of end-to-end onboarding/integration data and a catalogue of endpoints. + Use this API to access the Connecting Party Manager (CPM) service - an internal service for the creation and management of product IDs. + + This API only has a single access mode, application-restricted - signed JWT authentication, through which you may: + + * Create a product team + * Create a product + * Retrieve details about a product team + * Retrieve details about a product + * Retrieve a list of all products associated wit a team or all products associated with an organisation, grouped by product team + * Delete a product team + * Delete a product ## Who can use this API - This API can only be used where there is a legal basis to do so. Make sure you have a valid use case before you go too far with your development. You must demonstrate you have a valid use case as part of digital onboarding. Connecting Parties must have an appointed Clinical Safety Officer and undertake a Clinical Safety Assessment. + Currently this API is for [internal use only](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#statuses). ## API status and roadmap - This API is in [development](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#statuses). + This API is in development, meaning: - To see our roadmap, or to suggest, comment or vote on features for this API, see our interactive [product backlog](https://nhs-digital-api-management.featureupvote.com/). - - If you have any other queries, [contact us](https://digital.nhs.uk/developer/help-and-support). + * it is available for testing in the integration environment + * we expect to make breaking changes based on developer feedback ## Service level - This API is a platinum service, meaning it is operational and supported 24 hours a day, 365 days a year. + This API is a bronze service, meaning it is operational and supported only during business hours (8 am to 6 pm), Monday to Friday excluding bank holidays. For more details, see [service levels](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#service-levels). @@ -96,28 +105,7 @@ info: ## Onboarding - You need to get your software approved by us before it can go live with this API. We call this onboarding. The onboarding process can sometimes be quite long, so it’s worth planning well ahead. - - As part of this process, you need to demonstrate that you can manage risks and that your software conforms technically with the requirements for this API. - - Information on this page might impact the design of your software. For details, see [Onboarding support information](https://digital.nhs.uk/developer/api-catalogue/national-record-locator-consumer-fhir/onboarding-support-information). - - To understand how our online digital onboarding process works, see [digital onboarding](https://digital.nhs.uk/developer/guides-and-documentation/digital-onboarding#using-the-digital-onboarding-portal). - -
-
-
-
- - - -
-
-
-

To get started, sign in or create a developer account, then select 'product onboarding'.

-
-
-
+ This API is not yet available for onboarding. ## Change log diff --git a/infrastructure/swagger/05_paths.yaml b/infrastructure/swagger/05_paths.yaml index ebc750240..c9c563b45 100644 --- a/infrastructure/swagger/05_paths.yaml +++ b/infrastructure/swagger/05_paths.yaml @@ -30,7 +30,7 @@ paths: "200": $ref: "#/components/responses/StatusOK" "404": - $ref: "#/components/responses/NotFound" + description: _status not found parameters: - $ref: "#/components/parameters/HeaderVersion" - $ref: "#/components/parameters/HeaderRequestId" @@ -128,7 +128,7 @@ paths: "200": $ref: "#/components/responses/CPMProductTeamRead" "404": - $ref: "#/components/responses/NotFound" + $ref: "#/components/responses/ProductTeamNotFound" x-amazon-apigateway-integration: <<: *ApiGatewayIntegration uri: ${method_readProductTeam} @@ -151,7 +151,7 @@ paths: "200": $ref: "#/components/responses/CpmProductTeamDelete" "404": - $ref: "#/components/responses/NotFound" + $ref: "#/components/responses/ProductTeamNotFound" "409": $ref: "#/components/responses/Conflict" x-amazon-apigateway-integration: @@ -225,9 +225,9 @@ paths: "201": $ref: "#/components/responses/ProductCreate" "400": - $ref: "#/components/responses/BadRequest" + $ref: "#/components/responses/CreateProductBadRequest" "404": - $ref: "#/components/responses/NotFound" + $ref: "#/components/responses/ProductTeamNotFound" x-amazon-apigateway-integration: <<: *ApiGatewayIntegration uri: ${method_createCpmProduct} @@ -356,7 +356,7 @@ paths: "200": $ref: "#/components/responses/ProductRead" "404": - $ref: "#/components/responses/NotFound" + $ref: "#/components/responses/ProductNotFound" x-amazon-apigateway-integration: <<: *ApiGatewayIntegration uri: ${method_readCpmProduct} @@ -424,7 +424,7 @@ paths: "200": $ref: "#/components/responses/ProductDelete" "404": - $ref: "#/components/responses/NotFound" + $ref: "#/components/responses/ProductDeleteNotFound" x-amazon-apigateway-integration: <<: *ApiGatewayIntegration uri: ${method_deleteCpmProduct} diff --git a/infrastructure/swagger/07_components--schemas--domain.yaml b/infrastructure/swagger/07_components--schemas--domain.yaml index 4de100633..ba5b71dc8 100644 --- a/infrastructure/swagger/07_components--schemas--domain.yaml +++ b/infrastructure/swagger/07_components--schemas--domain.yaml @@ -81,10 +81,10 @@ components: type: string name: type: string - cpm_product_team_id: - type: string product_team_id: type: string + cpm_product_team_id: + type: string ods_code: type: string status: @@ -142,6 +142,8 @@ components: items: type: object properties: + cpm_product_team_id: + type: string product_team_id: type: string products: @@ -178,12 +180,13 @@ components: nullable: true example: results: - - org_code: "xyzzy" + - org_code: "F5H1R" product_teams: - - product_team_id: "1234" + - product_team_id: "55e86121-3826-468c-a6f0-dd0f1fbc0259" + cpm_product_team_id: "a9a9694d-001b-45ce-9f2a-6c9bf80ae0d0" products: - - id: "P.123" - product_team_id: "1234" + - id: "P.1X3-XYZ" + product_team_id: "55e86121-3826-468c-a6f0-dd0f1fbc0259" name: "My Great Product 1" ods_code: "F5H1R" status: "active" @@ -191,10 +194,11 @@ components: updated_on: "null" deleted_on: "null" keys: [] - - product_team_id: "5678" + - product_team_id: "24ac1857-f718-4905-813f-52da930c3ea1" + cpm_product_team_id: "152705aa-0342-487f-a654-64bba814a847" products: - - id: "P.xyz" - product_team_id: "5678" + - id: "P.4Y6-ABC" + product_team_id: "24ac1857-f718-4905-813f-52da930c3ea1" name: "My Great Product 3" ods_code: "F5H1R" status: "active" diff --git a/infrastructure/swagger/10_components--parameters--path.yaml b/infrastructure/swagger/10_components--parameters--path.yaml index f8118bfb3..9bfdba1cc 100644 --- a/infrastructure/swagger/10_components--parameters--path.yaml +++ b/infrastructure/swagger/10_components--parameters--path.yaml @@ -5,13 +5,13 @@ components: name: product_team_id in: path required: true - description: product team identifier + description: Product Team identifier. Either the "internally" generated id (cpm_product_team_id) or the provided team id (product_team_id) schema: type: string ProductId: name: product_id in: path required: true - description: product identifier + description: The Product identifier generated at time of creation. schema: type: string diff --git a/infrastructure/swagger/12_components--responses.yaml b/infrastructure/swagger/12_components--responses.yaml index cebc2af0d..82b05e9f6 100644 --- a/infrastructure/swagger/12_components--responses.yaml +++ b/infrastructure/swagger/12_components--responses.yaml @@ -20,8 +20,8 @@ components: $ref: "#/components/schemas/StatusOK" headers: <<: *ResponseHeaders - NotFound: - description: Not found + ProductTeamNotFound: + description: Product Team has not been found content: application/json: schema: @@ -29,7 +29,34 @@ components: example: errors: - code: "RESOURCE_NOT_FOUND" - message: "Could not find for key ('')" + message: "Could not find ProductTeam for key ('f9518c12-6c83-4544-97db-d9dd1d64da97')" + ProductNotFound: + description: Product has not been found + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - code: "RESOURCE_NOT_FOUND" + message: "Could not find CpmProduct for key ('P.XXX-YYY')" + ProductDeleteNotFound: + description: Product has not been found + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + examples: + ProductTeamNotFound: + value: + errors: + - code: "RESOURCE_NOT_FOUND" + message: "Could not find ProductTeam for key ('f9518c12-6c83-4544-97db-d9dd1d64da97')" + ProductNotFound: + value: + errors: + - code: "RESOURCE_NOT_FOUND" + message: "Could not find CpmProduct for key ('f9518c12-6c83-4544-97db-d9dd1d64da97', 'P.XXX-YYY')" Conflict: description: Conflict content: @@ -39,7 +66,7 @@ components: example: errors: - code: "CONFLICT" - message: "Product Team cannot be deleted as it still has associated Product Ids ['P.123-XYZ', 'P.456-ABC']" + message: "Product Team cannot be deleted as it still has associated Product Ids ['P.1X3-XYZ', 'P.4Y6-ABC']" BadRequest: description: Bad request (multiple error types) content: @@ -103,6 +130,33 @@ components: errors: - code: "MISSING_VALUE" message: "CreateProductTeamIncomingParams.ods_code: field required" + CreateProductBadRequest: + description: createProduct Bad request + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + examples: + MissingValue: + value: + errors: + - code: "MISSING_VALUE" + message: "CreateCpmProductIncomingParams.name: field required" + ExtraFieldsValidationError: + value: + errors: + - code: "VALIDATION_ERROR" + message: "CreateCpmProductIncomingParams.extra_name: extra fields not permitted" + EmptyValueValidationError: + value: + errors: + - code: "VALIDATION_ERROR" + message: "CreateCpmProductIncomingParams.name: ensure this value has at least 1 characters" + InvalidJSONValidationError: + value: + errors: + - code: "VALIDATION_ERROR" + message: "Invalid JSON body was provided: line 1 column 20 (char 19)" headers: Access-Control-Allow-Origin: schema: diff --git a/infrastructure/swagger/13_components--parameters--query.yaml b/infrastructure/swagger/13_components--parameters--query.yaml index f84cc9d8a..ddbd66769 100644 --- a/infrastructure/swagger/13_components--parameters--query.yaml +++ b/infrastructure/swagger/13_components--parameters--query.yaml @@ -5,7 +5,7 @@ components: name: product_team_id in: query required: false - description: The ID of the product team to filter results by. + description: Product Team identifier to filter results by. Either the "internally" generated id (cpm_product_team_id) or the provided team id (product_team_id) schema: type: string OrganisationCodeQuery: diff --git a/pyproject.toml b/pyproject.toml index 98c201739..731632935 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "connecting-party-manager" -version = "2025.03.07" +version = "2025.03.10" description = "Repository for the Connecting Party Manager API and related services" authors = ["NHS England"] license = "LICENSE.md" diff --git a/scripts/UI/ui.mk b/scripts/UI/ui.mk new file mode 100644 index 000000000..fe0396053 --- /dev/null +++ b/scripts/UI/ui.mk @@ -0,0 +1,4 @@ +.PHONY: run--ui + +run--ui: ## Run the CPM test UI on localhost + npm run --prefix test_ui/product_id_flow dev diff --git a/scripts/infrastructure/apigee/apigee.sh b/scripts/infrastructure/apigee/apigee.sh index a6d8cb917..272e6a334 100644 --- a/scripts/infrastructure/apigee/apigee.sh +++ b/scripts/infrastructure/apigee/apigee.sh @@ -100,6 +100,9 @@ function attach_product(){ _product_name="connecting-party-manager--$_apigee_environment--$API_NAME--app-level0" _secret_name="$_aws_environment--apigee-app-client-info" _apigee_stage=$(get_apigee_stage ${_workspace_name}) + external_id_secret_name="nhse-cpm--mgmt--${_aws_environment}-external-id" + external_id=$(aws secretsmanager get-secret-value --secret-id $external_id_secret_name --query SecretString --output text) + echo " @@ -118,7 +121,7 @@ function attach_product(){ session_name="attach-product-session" duration_seconds=900 - assume_role_output=$(aws sts assume-role --role-arn "$role_arn" --role-session-name "$session_name" --duration-seconds "$duration_seconds") + assume_role_output=$(aws sts assume-role --role-arn "$role_arn" --role-session-name "$session_name" --external-id "$external_id" --duration-seconds "$duration_seconds") # Check if the assume-role command was successful if [ $? -eq 0 ]; then @@ -183,6 +186,8 @@ function detach_product(){ _product_name="connecting-party-manager--$_apigee_environment--$API_NAME--app-level0" _secret_name="$_aws_environment--apigee-app-client-info" _apigee_stage=$(get_apigee_stage ${_workspace_name}) + external_id_secret_name="nhse-cpm--mgmt--${_aws_environment}-external-id" + external_id=$(aws secretsmanager get-secret-value --secret-id $external_id_secret_name --query SecretString --output text) echo " @@ -201,7 +206,7 @@ function detach_product(){ session_name="attach-product-session" duration_seconds=900 - assume_role_output=$(aws sts assume-role --role-arn "$role_arn" --role-session-name "$session_name" --duration-seconds "$duration_seconds") + assume_role_output=$(aws sts assume-role --role-arn "$role_arn" --role-session-name "$session_name" --external-id "$external_id" --duration-seconds "$duration_seconds") # Check if the assume-role command was successful if [ $? -eq 0 ]; then diff --git a/scripts/infrastructure/destroy/destroy-corrupted-workspace.sh b/scripts/infrastructure/destroy/destroy-corrupted-workspace.sh index 7f0f1428e..efc365de5 100644 --- a/scripts/infrastructure/destroy/destroy-corrupted-workspace.sh +++ b/scripts/infrastructure/destroy/destroy-corrupted-workspace.sh @@ -27,7 +27,10 @@ function _destroy_corrupted_workspace() { role_arn="arn:aws:iam::${dev_acct}:role/${TERRAFORM_ROLE_NAME}" session_name="resource-search-session" duration_seconds=900 - assume_role_output=$(aws sts assume-role --role-arn "$role_arn" --role-session-name "$session_name" --duration-seconds "$duration_seconds") + external_id_secret_name="nhse-cpm--mgmt--${ENV}-external-id" + external_id=$(aws secretsmanager get-secret-value --secret-id $external_id_secret_name --query SecretString --output text) + + assume_role_output=$(aws sts assume-role --role-arn "$role_arn" --role-session-name "$session_name" --external-id "$external_id" --duration-seconds "$duration_seconds") # Check if the assume-role command was successful if [ $? -eq 0 ]; then diff --git a/scripts/infrastructure/destroy/destroy-expired-workspaces.sh b/scripts/infrastructure/destroy/destroy-expired-workspaces.sh index 18999e727..9fadf990a 100644 --- a/scripts/infrastructure/destroy/destroy-expired-workspaces.sh +++ b/scripts/infrastructure/destroy/destroy-expired-workspaces.sh @@ -11,7 +11,11 @@ function _destroy_expired_workspaces() { role_arn="arn:aws:iam::${dev_acct}:role/NHSDeploymentRole" session_name="resource-search-session" duration_seconds=900 - assume_role_output=$(aws sts assume-role --role-arn "$role_arn" --role-session-name "$session_name" --duration-seconds "$duration_seconds") + + external_id_secret_name="nhse-cpm--mgmt--${ENV}-external-id" + external_id=$(aws secretsmanager get-secret-value --secret-id $external_id_secret_name --query SecretString --output text) + + assume_role_output=$(aws sts assume-role --role-arn "$role_arn" --role-session-name "$session_name" --external-id "$external_id" --duration-seconds "$duration_seconds") # Check if the assume-role command was successful if [ $? -eq 0 ]; then diff --git a/src/api/searchProduct/tests/test_index.py b/src/api/searchProduct/tests/test_index.py index 6a84b6987..5b88bd5bf 100644 --- a/src/api/searchProduct/tests/test_index.py +++ b/src/api/searchProduct/tests/test_index.py @@ -135,7 +135,8 @@ def test_search_by_product_team_id(): "org_code": ODS_CODE, "product_teams": [ { - "product_team_id": product_team.id, + "product_team_id": None, + "cpm_product_team_id": product_team.id, "products": [cpm_product.state()], }, ], @@ -209,7 +210,8 @@ def test_search_by_product_team_alias(): "org_code": ODS_CODE, "product_teams": [ { - "product_team_id": product_team.id, + "product_team_id": "808a36db-a52a-4130-b71e-d9cbcbaed15b", + "cpm_product_team_id": product_team.id, "products": [cpm_product.state()], }, ], @@ -284,7 +286,8 @@ def test_index_org_code(): "org_code": ODS_CODE, "product_teams": [ { - "product_team_id": product_team.id, + "product_team_id": "808a36db-a52a-4130-b71e-d9cbcbaed15b", + "cpm_product_team_id": product_team.id, "products": [cpm_product.state()], }, ], @@ -419,11 +422,13 @@ def test_index_org_code_multiple_product_teams(): "org_code": ODS_CODE, "product_teams": [ { - "product_team_id": product_team_1.id, + "product_team_id": "808a36db-a52a-4130-b71e-d9cbcbaed15b", + "cpm_product_team_id": product_team_1.id, "products": [cpm_product_1.state()], }, { - "product_team_id": product_team_2.id, + "product_team_id": None, + "cpm_product_team_id": product_team_2.id, "products": [cpm_product_2.state()], }, ], @@ -434,9 +439,8 @@ def test_index_org_code_multiple_product_teams(): # Ensure expected product teams are sorted expected_product_teams = sorted( expected_response["results"][0]["product_teams"], - key=lambda team: team["product_team_id"], + key=lambda team: team["cpm_product_team_id"], ) - # Sort the products within each product team in the expected response for team in expected_product_teams: team["products"] = sorted(team["products"], key=lambda product: product["id"]) diff --git a/src/api/tests/feature_tests/features/searchCpmProduct.success.feature b/src/api/tests/feature_tests/features/searchCpmProduct.success.feature index 8cda8cbd8..0a0bf0396 100644 --- a/src/api/tests/feature_tests/features/searchCpmProduct.success.feature +++ b/src/api/tests/feature_tests/features/searchCpmProduct.success.feature @@ -36,11 +36,12 @@ Feature: Search Products - success scenarios | path | value | | name | My Great Product | And I note the response field "$.id" as "product_id" - When I make a "GET" request with "default" headers to "Product?product_team_id=${ note(product_team_id) }" + When I make a "GET" request with "default" headers to "Product?product_team_id=" Then I receive a status code "200" with body | path | value | | results.0.org_code | F5H1R | - | results.0.product_teams.0.product_team_id | ${ note(product_team_id) } | + | results.0.product_teams.0.product_team_id | 808a36db-a52a-4130-b71e-d9cbcbaed15b | + | results.0.product_teams.0.cpm_product_team_id | ${ note(product_team_id) } | | results.0.product_teams.0.products.0.id | ${ note(product_id) } | | results.0.product_teams.0.products.0.cpm_product_team_id | ${ note(product_team_id) } | | results.0.product_teams.0.products.0.product_team_id | 808a36db-a52a-4130-b71e-d9cbcbaed15b | @@ -54,7 +55,7 @@ Feature: Search Products - success scenarios And the response headers contain: | name | value | | Content-Type | application/json | - | Content-Length | 444 | + | Content-Length | 507 | Examples: | product_team_id | @@ -77,7 +78,8 @@ Feature: Search Products - success scenarios Then I receive a status code "200" with body | path | value | | results.0.org_code | F5H1R | - | results.0.product_teams.0.product_team_id | ${ note(product_team_id) } | + | results.0.product_teams.0.product_team_id | 808a36db-a52a-4130-b71e-d9cbcbaed15b | + | results.0.product_teams.0.cpm_product_team_id | ${ note(product_team_id) } | | results.0.product_teams.0.products.0.id | ${ note(product_id) } | | results.0.product_teams.0.products.0.cpm_product_team_id | ${ note(product_team_id) } | | results.0.product_teams.0.products.0.product_team_id | 808a36db-a52a-4130-b71e-d9cbcbaed15b | @@ -91,7 +93,7 @@ Feature: Search Products - success scenarios And the response headers contain: | name | value | | Content-Type | application/json | - | Content-Length | 444 | + | Content-Length | 507 | Scenario Outline: Successfully search more than one Product with product team id or alias Given I have already made a "POST" request with "default" headers to "ProductTeam" with body: @@ -117,7 +119,8 @@ Feature: Search Products - success scenarios Then I receive a status code "200" with body where ProductTeams has a length of "1" with "3" Products each | path | value | | results.0.org_code | F5H1R | - | results.0.product_teams.0.product_team_id | ${ note(product_team_id) } | + | results.0.product_teams.0.product_team_id | 808a36db-a52a-4130-b71e-d9cbcbaed15b | + | results.0.product_teams.0.cpm_product_team_id | ${ note(product_team_id) } | | results.0.product_teams.0.products.0.id | ${ note(product_id_1) } | | results.0.product_teams.0.products.0.cpm_product_team_id | ${ note(product_team_id) } | | results.0.product_teams.0.products.0.product_team_id | 808a36db-a52a-4130-b71e-d9cbcbaed15b | @@ -151,7 +154,7 @@ Feature: Search Products - success scenarios And the response headers contain: | name | value | | Content-Type | application/json | - | Content-Length | 1078 | + | Content-Length | 1141 | Examples: | product_team_id | @@ -181,7 +184,8 @@ Feature: Search Products - success scenarios Then I receive a status code "200" with body where ProductTeams has a length of "2" with "2" Products each | path | value | | results.0.org_code | F5H1R | - | results.0.product_teams.0.product_team_id | ${ note(product_team_id_1) } | + | results.0.product_teams.0.product_team_id | null | + | results.0.product_teams.0.cpm_product_team_id | ${ note(product_team_id_1) } | | results.0.product_teams.0.products.0.id | ${ note(product_id_1) } | | results.0.product_teams.0.products.0.cpm_product_team_id | ${ note(product_team_id_1) } | | results.0.product_teams.0.products.0.product_team_id | null | @@ -192,7 +196,8 @@ Feature: Search Products - success scenarios | results.0.product_teams.0.products.0.updated_on | null | | results.0.product_teams.0.products.0.deleted_on | null | | results.0.product_teams.0.products.0.keys | [] | - | results.0.product_teams.1.product_team_id | ${ note(product_team_id_2) } | + | results.0.product_teams.1.product_team_id | null | + | results.0.product_teams.1.cpm_product_team_id | ${ note(product_team_id_2) } | | results.0.product_teams.1.products.0.id | ${ note(product_id_2) } | | results.0.product_teams.1.products.0.cpm_product_team_id | ${ note(product_team_id_2) } | | results.0.product_teams.1.products.0.product_team_id | null | @@ -206,7 +211,7 @@ Feature: Search Products - success scenarios And the response headers contain: | name | value | | Content-Type | application/json | - | Content-Length | 769 | + | Content-Length | 827 | Scenario: Deleted Products not returned in search Given I have already made a "POST" request with "default" headers to "ProductTeam" with body: @@ -233,7 +238,8 @@ Feature: Search Products - success scenarios Then I receive a status code "200" with body where ProductTeams has a length of "1" with "2" Products each | path | value | | results.0.org_code | F5H1R | - | results.0.product_teams.0.product_team_id | ${ note(product_team_id) } | + | results.0.product_teams.0.product_team_id | 808a36db-a52a-4130-b71e-d9cbcbaed15b | + | results.0.product_teams.0.cpm_product_team_id | ${ note(product_team_id) } | | results.0.product_teams.0.products.0.id | ${ note(product_id_1) } | | results.0.product_teams.0.products.0.cpm_product_team_id | ${ note(product_team_id) } | | results.0.product_teams.0.products.0.product_team_id | 808a36db-a52a-4130-b71e-d9cbcbaed15b | @@ -257,4 +263,4 @@ Feature: Search Products - success scenarios And the response headers contain: | name | value | | Content-Type | application/json | - | Content-Length | 762 | + | Content-Length | 825 | diff --git a/src/api/tests/feature_tests/steps/steps.py b/src/api/tests/feature_tests/steps/steps.py index d7c16d59c..3a826c60c 100644 --- a/src/api/tests/feature_tests/steps/steps.py +++ b/src/api/tests/feature_tests/steps/steps.py @@ -235,11 +235,11 @@ def then_response( # Sort product teams by "product_team_id" expected_body["results"][0]["product_teams"] = sorted( expected_body["results"][0]["product_teams"], - key=lambda team: team["product_team_id"], + key=lambda team: team["cpm_product_team_id"], ) response_body["results"][0]["product_teams"] = sorted( response_body["results"][0]["product_teams"], - key=lambda team: team["product_team_id"], + key=lambda team: team["cpm_product_team_id"], ) # Sort products inside each product team by "id" diff --git a/src/layers/domain/response/response_models.py b/src/layers/domain/response/response_models.py index cdb9da87f..959c772d3 100644 --- a/src/layers/domain/response/response_models.py +++ b/src/layers/domain/response/response_models.py @@ -19,18 +19,20 @@ def _group_products(self, products: list[dict]) -> list[dict]: for product in product_dicts: org_code = product["ods_code"] - team_id = product["cpm_product_team_id"] + product_team_id = product["product_team_id"] + cpm_team_id = product["cpm_product_team_id"] if org_code not in organisations: organisations[org_code] = {"org_code": org_code, "product_teams": {}} - if team_id not in organisations[org_code]["product_teams"]: - organisations[org_code]["product_teams"][team_id] = { - "product_team_id": team_id, + if cpm_team_id not in organisations[org_code]["product_teams"]: + organisations[org_code]["product_teams"][cpm_team_id] = { + "product_team_id": product_team_id, + "cpm_product_team_id": cpm_team_id, "products": [], } - organisations[org_code]["product_teams"][team_id]["products"].append( + organisations[org_code]["product_teams"][cpm_team_id]["products"].append( product ) @@ -41,7 +43,7 @@ def _group_products(self, products: list[dict]) -> list[dict]: "org_code": org["org_code"], "product_teams": sorted( list(org["product_teams"].values()), - key=lambda team: team["product_team_id"], + key=lambda team: team["cpm_product_team_id"], ), } for org in organisations.values() diff --git a/test_ui/product_id_flow/index.html b/test_ui/product_id_flow/index.html index 1ba4c5ab4..8ce2d0b72 100644 --- a/test_ui/product_id_flow/index.html +++ b/test_ui/product_id_flow/index.html @@ -3,7 +3,7 @@ - NHS Product Team Creation + NHS Product ID demos