From a60f35ef66d38eb4a21cc82bde4812180c0f25a8 Mon Sep 17 00:00:00 2001 From: stuart mcneill Date: Mon, 2 Jun 2025 23:17:59 +0100 Subject: [PATCH 01/13] Change AcsService getUserRoleCodeAndId() to throw a BadRequestException if we are unable to retrieve the users job role code --- .gitignore | 2 ++ .../uk/nhs/adaptors/scr/services/AcsService.java | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d126e2f8..bc940fd0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ __pycache__/ .venv/ smoketest-report.xml *.code-workspace +.python-version + diff --git a/docker/service/src/main/java/uk/nhs/adaptors/scr/services/AcsService.java b/docker/service/src/main/java/uk/nhs/adaptors/scr/services/AcsService.java index a1115553..3726bdb9 100644 --- a/docker/service/src/main/java/uk/nhs/adaptors/scr/services/AcsService.java +++ b/docker/service/src/main/java/uk/nhs/adaptors/scr/services/AcsService.java @@ -90,12 +90,22 @@ private Pair getUserRoleCodeAndId(String authorisation, String n } + // if we haven't retrieved a role code from openID we try the SDS service try { - return Pair.of(sdsService.getUserRoleCode(nhsdSessionUrid), nhsdIdentity); + var roleCode = sdsService.getUserRoleCode(nhsdSessionUrid); + if (roleCode != null && !roleCode.isEmpty()) { + return Pair.of(roleCode, nhsdIdentity); + } } catch (BadRequestException | URISyntaxException e) { + LOGGER.info(String.format("Unable to determine Job Role Code for " + + "the given RoleID via the SDS Service: %s", nhsdSessionUrid)); throw new BadRequestException(String.format("Unable to determine SDS Job Role Code for " + "the given RoleID: %s", nhsdSessionUrid)); } + LOGGER.info(String.format("Unable to determine Job Role Code for " + + "the given RoleID via the SDS Service: %s", nhsdSessionUrid)); + throw new BadRequestException(String.format("Unable to determine SDS Job Role Code for " + + "the given RoleID: %s", nhsdSessionUrid)); } private String prepareAcsRequest(ParametersParameterComponent parameter, RequestData requestData, String sdsJobRoleCode, From 7f37ac42bbab14ce3c078d876e84f4970071f1d0 Mon Sep 17 00:00:00 2001 From: stuart mcneill Date: Fri, 6 Jun 2025 09:23:31 +0100 Subject: [PATCH 02/13] Changed GetScrIdUAT to return 400 if unable to retrieve user role code - corrected path problem when building attachment resource url - corrected documentation to advise using combined auth-authentication --- .../uk/nhs/adaptors/scr/uat/GetScrIdUAT.java | 44 ++--- .../uk/nhs/adaptors/scr/uat/SetAcsUAT.java | 24 +++ .../adaptors/scr/services/GetScrService.java | 22 +-- specification/summary-care-record.yaml | 184 +++++++++--------- 4 files changed, 147 insertions(+), 127 deletions(-) diff --git a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/GetScrIdUAT.java b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/GetScrIdUAT.java index fedf49c1..b3b8b7a8 100644 --- a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/GetScrIdUAT.java +++ b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/GetScrIdUAT.java @@ -43,10 +43,10 @@ @SpringBootTest @AutoConfigureMockMvc -@ExtendWith({SpringExtension.class}) +@ExtendWith({ SpringExtension.class }) @DirtiesContext @Slf4j -@ContextConfiguration(initializers = {WireMockInitializer.class}) +@ContextConfiguration(initializers = { WireMockInitializer.class }) public class GetScrIdUAT { private static final String EVENT_LIST_QUERY_HEADER = "urn:nhs:names:services:psisquery/QUPC_IN180000SM04"; @@ -57,11 +57,11 @@ public class GetScrIdUAT { private static final String TYPE_PARAM = "http://snomed.info/sct|196981000000101"; private static final String NHSD_ASID = "1029384756"; private static final String CLIENT_IP = "192.168.0.24"; - private static final String[] IGNORED_JSON_PATHS = new String[]{ - "id", - "entry[*].fullUrl", - "entry[*].resource.subject.reference", - "entry[*].resource.id" + private static final String[] IGNORED_JSON_PATHS = new String[] { + "id", + "entry[*].fullUrl", + "entry[*].resource.subject.reference", + "entry[*].resource.id" }; @Value("classpath:uat/responses/event-list-query/success.xml") @@ -113,24 +113,24 @@ void testRetrieveLatestScrIdNoConsent(TestData testData) throws Exception { private void performRequestAndAssert(TestData testData, HttpStatus expectedHttpStatus) throws Exception { mockMvc.perform(get(GET_SCR_ID_ENDPOINT) - .contentType(APPLICATION_FHIR_JSON_VALUE) - .header(ScrHttpHeaders.NHSD_ASID, NHSD_ASID) - .header(ScrHttpHeaders.CLIENT_IP, CLIENT_IP) - .queryParam("patient", NHS_NUMBER) - .queryParam("type", TYPE_PARAM) - .queryParam("_sort", SORT_PARAM) - .queryParam("_count", COUNT_PARAM)) - .andExpect(status().is(expectedHttpStatus.value())) - .andExpect(fhirJson(testData.getFhirResponse(), IGNORED_JSON_PATHS)); + .contentType(APPLICATION_FHIR_JSON_VALUE) + .header(ScrHttpHeaders.NHSD_ASID, NHSD_ASID) + .header(ScrHttpHeaders.CLIENT_IP, CLIENT_IP) + .queryParam("patient", NHS_NUMBER) + .queryParam("type", TYPE_PARAM) + .queryParam("_sort", SORT_PARAM) + .queryParam("_count", COUNT_PARAM)) + .andExpect(status().is(expectedHttpStatus.value())) + .andExpect(fhirJson(testData.getFhirResponse(), IGNORED_JSON_PATHS)); } private void stubSpinePsisEndpoint(Resource response) throws IOException { wireMockServer.stubFor( - WireMock.post(spineConfiguration.getPsisQueriesEndpoint()) - .withHeader(SOAP_ACTION, equalTo(EVENT_LIST_QUERY_HEADER)) - .withHeader(CONTENT_TYPE, equalTo(TEXT_XML_VALUE)) - .willReturn(aResponse() - .withStatus(OK.value()) - .withBody(readString(response.getFile().toPath(), UTF_8)))); + WireMock.post(spineConfiguration.getPsisQueriesEndpoint()) + .withHeader(SOAP_ACTION, equalTo(EVENT_LIST_QUERY_HEADER)) + .withHeader(CONTENT_TYPE, equalTo(TEXT_XML_VALUE)) + .willReturn(aResponse() + .withStatus(OK.value()) + .withBody(readString(response.getFile().toPath(), UTF_8)))); } } diff --git a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java index 5bd1d283..07091a34 100644 --- a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java +++ b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java @@ -121,6 +121,19 @@ public void testSetAcsPermissionBadRequest(TestData testData) throws Exception { .andExpect(content().json(testData.getFhirResponse())); } + @ParameterizedTest(name = "[{index}] - {0}") + @ArgumentsSource(SetAcsBadRequest.class) + public void testSetAcsPermissionNoRoleCodeBadRequest(TestData testData) throws Exception { + // FLAGSAPI-1046 should return Bad Request if no role code is returned from SDS or Identity Service + stubFailedIdentityService(); + stubFailedSdsService(); + stubSpineAcsEndpoint(acsErrorResponse); + + performRequest(testData.getFhirRequest()) + .andExpect(status().isBadRequest()) + .andExpect(content().json(testData.getFhirResponse())); + } + private ResultActions performRequest(String request) throws Exception { return mockMvc.perform(post(ACS_ENDPOINT) .contentType(APPLICATION_FHIR_JSON) @@ -154,6 +167,17 @@ private void stubSdsService(Resource response) throws IOException { .withBody(readString(response.getFile().toPath(), UTF_8)))); } + private void stubFailedSdsService(Resource response) throws IOException { + wireMockServer.stubFor( + WireMock.get(WireMock.urlPathEqualTo(PRACTITIONER_ROLE_ENDPOINT)) + .withQueryParam(USER_ID_QUERY_PARAM, + containing(NHSD_SESSION_URID)) + .willReturn(aResponse() + .withStatus(OK.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withBody(readString(response.getFile().toPath(), UTF_8)))); + } + private void stubIdentityService(Resource response) throws IOException { wireMockServer.stubFor( WireMock.get(USER_INFO_ENDPOINT) diff --git a/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java b/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java index 6fcad968..28fbed39 100644 --- a/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java +++ b/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java @@ -80,7 +80,7 @@ public class GetScrService { private static final String SNOMED_SYSTEM = "http://snomed.info/sct"; private static final String GP_SUMMARY_SNOMED_CODE = "196981000000101"; private static final String GP_SUMMARY_DISPLAY = " General Practice Summary"; - private static final String ATTACHMENT_URL = "%s/Bundle?composition.identifier=%s" + private static final String ATTACHMENT_URL = "%sBundle?composition.identifier=%s" + "&composition.subject:Patient.identifier=https://fhir.nhs.uk/Id/nhs-number|%s"; private static final CodeableConcept GP_SUMMARY_SNOMED = new CodeableConcept(new Coding() @@ -133,13 +133,11 @@ public Bundle getScrId(String nhsNumber, String nhsdAsid, String clientIp) { bundle.addEntry(new BundleEntryComponent() .setFullUrl(getScrUrl() + "/DocumentReference/" + documentReference.getId()) .setResource(documentReference) - .setSearch(new Bundle.BundleEntrySearchComponent().setMode(MATCH)) - ); + .setSearch(new Bundle.BundleEntrySearchComponent().setMode(MATCH))); bundle.addEntry(new BundleEntryComponent() .setFullUrl(patient.getId()) - .setResource(patient) - ); + .setResource(patient)); } else { bundle.setTotal(0); } @@ -235,8 +233,8 @@ private void checkDetectedIssues(Document document) { } private DocumentReference buildDocumentReference(String nhsNumber, - EventListQueryResponse response, - Patient patient) { + EventListQueryResponse response, + Patient patient) { DocumentReference documentReference = new DocumentReference(); documentReference.setId(randomUUID()); @@ -248,8 +246,7 @@ private DocumentReference buildDocumentReference(String nhsNumber, documentReference.setType(GP_SUMMARY_SNOMED); documentReference.setSubject(new Reference(patient)); - DocumentReferenceContentComponent content = - buildDocumentReferenceContent(nhsNumber, response.getLatestScrId()); + DocumentReferenceContentComponent content = buildDocumentReferenceContent(nhsNumber, response.getLatestScrId()); documentReference.addContent(content); documentReference.setMasterIdentifier(new Identifier() @@ -270,7 +267,6 @@ private DocumentReferenceContentComponent buildDocumentReferenceContent(String n return content; } - private Patient buildPatientResource(String nhsNumber) { Patient patient = new Patient(); String patientResourceId = randomUUID(); @@ -285,7 +281,8 @@ private Patient buildPatientResource(String nhsNumber) { private String prepareEventListQueryRequest(String nhsNumber, String nhsdAsid, String clientIp) { EventListQueryParams eventListQueryParams = new EventListQueryParams() .setGeneratedMessageId(MDC.get(CORRELATION_ID_MDC_KEY)) - .setMessageCreationTime(DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now(ZoneId.of("Europe/London")))) + .setMessageCreationTime( + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now(ZoneId.of("Europe/London")))) .setNhsNumber(nhsNumber) .setSenderFromASID(nhsdAsid) .setSpineToASID(scrConfiguration.getNhsdAsidTo()) @@ -297,7 +294,8 @@ private String prepareEventListQueryRequest(String nhsNumber, String nhsdAsid, S private String prepareEventQueryRequest(String psisEventId, String nhsNumber, String nhsdAsid, String clientIp) { var eventListQueryParams = new EventQueryParams() .setGeneratedMessageId(MDC.get(CORRELATION_ID_MDC_KEY)) - .setMessageCreationTime(DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now(ZoneId.of("Europe/London")))) + .setMessageCreationTime( + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now(ZoneId.of("Europe/London")))) .setNhsNumber(nhsNumber) .setSenderFromASID(nhsdAsid) .setSpineToASID(scrConfiguration.getNhsdAsidTo()) diff --git a/specification/summary-care-record.yaml b/specification/summary-care-record.yaml index 87b1ad8d..0bba7f06 100644 --- a/specification/summary-care-record.yaml +++ b/specification/summary-care-record.yaml @@ -3,7 +3,7 @@ info: version: 1.0.0 title: Summary Care Record API description: | - +
@@ -18,13 +18,13 @@ info:
- + ## Version The current released version of this API is 3.0 while minor updates may be available on PTL environments. ## Overview Use this API to access or update a patient's [Summary Care Record (SCR)](https://digital.nhs.uk/services/summary-care-records-scr) - an electronic record of important patient information, created from GP medical records. SCRs can be seen and used by authorised staff in other areas of the health and care system involved in the patient's direct care. - + This API is currently only approved for use in primary care software, specifically GP software. We hope to make it available for secondary care in the future. You can vote for this on our [interactive product backlog](https://nhs-digital-api-management.featureupvote.com/suggestions/151217/summary-care-record-scr-fhir-api-other-use-cases). Also use this API to update a patient's SCR Consent Preference on the Spine ACS and to raise electronic Alerts when: @@ -40,12 +40,12 @@ info: * upload a patient's SCR * send a privacy alert message, if you have to override a patient's dissent to view their SCR * update a patient's SCR Consent Preference on the Spine ACS - + A health or care staff providing direct care to patients must be present and authenticated with an [NHS smartcard or a modern alternative](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/nhs-smartcards-for-developers) to use this API. - + ## 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 do this before you can go live (see 'Onboarding' below). - + This API is initially for use by new market entrant GP IT developers with other use cases to follow later. ## Related APIs @@ -53,12 +53,12 @@ info: - [Personal Demographics Service (FHIR) API](https://digital.nhs.uk/developer/api-catalogue/personal-demographics-service-fhir) - use this API to search for patients and retrieve their details. This API can also be used to update their details in some cases. This is the latest version of the PDS API and is recommended for all new integrators. This API has endpoints enabling you to get and set access permissions, the same as the [Access Control Service (ACS) HL7 V3 API](https://digital.nhs.uk/developer/api-catalogue/access-control-service-hl7-v3) which it partly replaces. - + ## API status and roadmap This API is initially for use by new market entrant GP IT developers with other use cases to follow later. - + This API is [in production, beta](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#statuses), meaning: - + - the API is available in our sandbox and integration test environments - the API is available for production (private beta) use - we might make breaking changes, but only if we cannot avoid it, and we'll give advance notice @@ -73,7 +73,7 @@ info: It conforms to the [FHIR](https://digital.nhs.uk/developer/guides-and-documentation/our-api-technologies#fhir) global standard for health care data exchange, specifically to [FHIR R4 (v4.0.1)](https://hl7.org/fhir/r4/), except that it does not support the [capabilities](https://hl7.org/fhir/R4/http.html#capabilities) interaction. It includes some country-specific FHIR extensions, which are built against [FHIR UK Core](https://digital.nhs.uk/services/fhir-uk-core), specifically [UK.core.r4.v2 2.0.5](https://simplifier.net/packages/uk.core.r4.v2/2.0.5). - + The interactions between the SCR FHIR API and Spine have been tested with a wide range of special characters including emojis with appropriate Fitzpatrick modifiers. In accordance with HL7-FHIR specifications, this API operates with UTF-8 encoding. ## Network access @@ -88,21 +88,19 @@ info: - a health or care staff providing direct care to patients - strongly authenticated, using either an [NHS smartcard or a modern alternative](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/nhs-smartcards-for-developers) available via [NHS Care Identity Service 2 (NHS CIS2)](https://digital.nhs.uk/services/nhs-identity) - The API uses OAuth 2.0 to authorise the calling system. It supports the following security patterns: - + The API uses OAuth 2.0 to authorise the calling system. It only supports CIS2 combined authentication and authorisation (see link below). Do not use separate authentication and authorisation: - [user-restricted RESTful API - using NHS CIS2 - combined authentication and authorisation](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/user-restricted-restful-apis-nhs-cis2-combined-authentication-and-authorisation) - - [user-restricted RESTful API - using NHS CIS2 - separate authentication and authorisation](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/user-restricted-restful-apis-nhs-cis2-separate-authentication-and-authorisation) For more details, see [user-restricted APIs](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation#user-restricted-apis). The second authorisation method is [application-restricted](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation#application-restricted-apis) (signed JWT authentication), meaning a few specific API calls can be authorised by the application making the requests. This is typically provided so that GPs can perform batch updates of multiple Summary Care Records without having to log in as a specific user. - + The following specific endpoint and method combinations can be used with application-restricted authentication, in addition to user-restricted authentication: - + - GET DocumentReference - GET Bundle - POST Bundle - + For more details, see: - [application-restricted APIs](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation#application-restricted-apis) and - [application-restricted RESTful APIs - signed with JWT authentication](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/application-restricted-restful-apis-signed-jwt-authentication) @@ -113,10 +111,10 @@ info: | Sandbox | `https://sandbox.api.service.nhs.uk/summary-care-record/FHIR/R4`| | Integration test | `https://int.api.service.nhs.uk/summary-care-record/FHIR/R4` | | Production | `https://api.service.nhs.uk/summary-care-record/FHIR/R4` | - + ## NME Testing The approach to testing for external developers is as follows. - + For sandbox, New Market Entrants (NMEs) should perform Self Testing, followed by Integration Testing which will be available on the Assurance Technical Evidencing. NMEs need to meet these to progress in integration. ### Sandbox testing @@ -145,18 +143,18 @@ info: * is for formal use in a live environment * is stateful, so persists updates * includes authorisation, with [smartcard](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/nhs-smartcards-for-developers) - + For more information about our Production Environment, contact the [National Service Desk](ssd.nationalservicedesk@nhs.net) ## 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/summary-care-record-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). - +
@@ -171,31 +169,31 @@ info:
- + ### Troubleshooting - + * After setting up your new application through the NHS Developer Portal and connecting it to the Summary Care Record API, you will need to ensure your application has a valid ASID custom attribute. ITOC support services should be able to ensure your ASID values are setup to use the [QUPC_IN190000UK04](https://data.developer.nhs.uk/dms/mim/4.2.00/Domains/PSIS%20Query/Document%20files/PSIS%20Query%20IM.htm#_Toc_Section_6.2) interaction. This interaction is for access to resources on Personal Spine Information Service (PSIS), the former name of Summary Care Record. If this isn't setup, you may encounter 403 Forbidden errors when accessing records. This can be checked by emailing [itoc.supportdesk@nhs.net](mailto:itoc.supportdesk@nhs.net). * ITOC should also be able to apply the GP summary v3 message set to the endpoints for you. - + ## Errors We use standard HTTP status codes to show whether an API request succeeded or not. They are usually in the range: * 200 to 299 if it succeeded, including code 202 if it was accepted by an API that needs to wait for further action * 400 to 499 if it failed because of a client error by your application * 500 to 599 if it failed because of an error on our server - + Errors specific to each API are shown in the Endpoints section, under Response. See our [reference guide](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#http-status-codes) for more on errors. - + ## Exponential Backoff In the event of a transitory error: - + * 429 - exceeded application rate limit (5 TPS per application) * 500 - internal server error * 503 - service unavailable * 504 - response timed out - + The following retries are recommended: - + * Attempt 1: 1 minute after first failure * Attempt 2: 30 minutes after attempt 1 * Attempt 3: 60 minutes after attempt 2 @@ -204,10 +202,10 @@ info: * Attempt 6: 8 hours after attempt 5 * Attempt 7: 16 hours after attempt 6 * If attempt 7 fails, then fail permanently. - + ## Support The National Integration Adaptors & APIs (NIA) Support team provide a support mailbox to support new market entrants integrating with this adaptor. To raise a support ticket please contact [niasupport@nhs.net](mailto:niasupport@nhs.net) - + ## Operations Here are the links to the possible SCR operations. These endpoints are for create and consume use cases for GP systems: * [Get patient's Summary Care Record](#get-/Bundle) @@ -220,7 +218,7 @@ info: name: MIT contact: name: Summary Care Record FHIR API Support - url: 'https://digital.nhs.uk/developer/help-and-support' + url: "https://digital.nhs.uk/developer/help-and-support" email: api.management@nhs.net x-nhsd-api-platform: meta: @@ -230,23 +228,23 @@ x-nhsd-api-platform: description: This is a generated template API pipeline_name_prefix: "Summary-Care-Record" servers: - - url: 'https://sandbox.api.service.nhs.uk/summary-care-record/FHIR/R4' + - url: "https://sandbox.api.service.nhs.uk/summary-care-record/FHIR/R4" description: Sandbox environment - - url: 'https://int.api.service.nhs.uk/summary-care-record/FHIR/R4' + - url: "https://int.api.service.nhs.uk/summary-care-record/FHIR/R4" description: Integration test environment. x-spec-publication: operation-order: - - operations: - - method: GET - path: /DocumentReference - - method: GET - path: /Bundle - - method: POST - path: /Bundle - - method: POST - path: /AuditEvent - - method: POST - path: /$setPermission + - operations: + - method: GET + path: /DocumentReference + - method: GET + path: /Bundle + - method: POST + path: /Bundle + - method: POST + path: /AuditEvent + - method: POST + path: /$setPermission paths: /Bundle: post: @@ -257,26 +255,26 @@ paths: To execute the upload you must provide patient's NHS number. In case of an update, before executing the request, latest UUID must be obtained using GET /DocumentReference endpoint and set inside the request body. - + ## Usage diagram ![POST SCR Diagram](https://raw.githubusercontent.com/NHSDigital/summary-care-record-api/various-documentation-changes/specification/components/resources/images/diagram-1.jpg) ## Sandbox test scenarios You can test the following scenarios in our sandbox environment: - + | Scenario | Request | Response | | ----------------------------------| ------------------------------------------------- | -------------------- | | Happy path | `Patient.identifier.value`=`9000000009` | HTTP Status 201 | | No patient's consent to store SCR | `Patient.identifier.value`=`9111231130` | HTTP Status 403 | - + summary: Upload patient's Summary Care Record operationId: upload-scr parameters: - - $ref: '#/components/parameters/BearerAuthorization' - - $ref: '#/components/parameters/CorrelationID' - - $ref: '#/components/parameters/RequestID' - - $ref: '#/components/parameters/RoleID' - - $ref: '#/components/parameters/ContentType' + - $ref: "#/components/parameters/BearerAuthorization" + - $ref: "#/components/parameters/CorrelationID" + - $ref: "#/components/parameters/RequestID" + - $ref: "#/components/parameters/RoleID" + - $ref: "#/components/parameters/ContentType" requestBody: required: true content: @@ -366,9 +364,9 @@ paths: You must specify the NHS number and the UUID of the latest Summary Care Record of this patient. This UUID should be obtained using the GET /DocumentReference endpoint. If you specify an out-of-date UUID, this endpoint returns a Bundle with 0 entries. - + ## Usage diagram - + ![GET SCR Diagram](https://raw.githubusercontent.com/NHSDigital/summary-care-record-api/various-documentation-changes/specification/components/resources/images/diagram-2.jpg) ## Sandbox test scenarios @@ -379,13 +377,13 @@ paths: | Happy path | `composition.identifier`=`FA60BE64-1F34-11EB-A2A8-000C29A364EB` `composition.subject:Patient.identifier`=`9000000009` | HTTP Status 200 - Full Bundle | | No results | `composition.identifier`=`81CC2DA0-8882-11EB-B538-0800200C9A66` `composition.subject:Patient.identifier`=`9000000033` | HTTP Status 200 - Empty Bundle | | Invalid results | `composition.identifier`=`INVALID ID` `composition.subject:Patient.identifier`=`INVALID NHS NUMBER` | HTTP Status 400 - Invalid Request | - + summary: Get patient's Summary Care Record operationId: get-scr parameters: - - $ref: '#/components/parameters/BearerAuthorization' - - $ref: '#/components/parameters/CorrelationID' - - $ref: '#/components/parameters/RequestID' + - $ref: "#/components/parameters/BearerAuthorization" + - $ref: "#/components/parameters/CorrelationID" + - $ref: "#/components/parameters/RequestID" - in: query name: composition.identifier description: Latest Patient's Summary Care Record identifier. Can be obtained using GET /DocumentReference endpoint. @@ -472,7 +470,7 @@ paths: Use this endpoint to retrieve UUID of patient's latest record. The UUID is required to retrieve the Summary Care Record details using GET /Bundle endpoint and to update the details of patient's Summary Care Record using POST /Bundle endpoint. - + This endpoint also returns patient's consent status in the securityLabel attribute of a successful API response. To get the information you must provide patient's NHS number. @@ -480,18 +478,18 @@ paths: ## Sandbox test scenarios You can test the following scenarios in our sandbox environment: - + | Scenario | Request | Response | | ----------------------------------| --------------------------------- | ---------------------------------- | | Happy path | `nhs-number=9000000009` | HTTP Status 200 - Full Bundle | | Empty result | `nhs-number=9000000033` | HTTP Status 200 - Empty Bundle | | Invalid results | `nhs-number=INVALID NHS NUMBER` | HTTP Status 400 - Invalid Request | - + parameters: - - $ref: '#/components/parameters/BearerAuthorization' - - $ref: '#/components/parameters/CorrelationID' - - $ref: '#/components/parameters/RequestID' - - $ref: '#/components/parameters/RoleID' + - $ref: "#/components/parameters/BearerAuthorization" + - $ref: "#/components/parameters/CorrelationID" + - $ref: "#/components/parameters/RequestID" + - $ref: "#/components/parameters/RoleID" - in: query name: patient description: The patient's NHS number. Must be preceded with FHIR identifier (eg."patient=https://fhir.nhs.uk/Id/nhs-number|9000000009") @@ -499,7 +497,7 @@ paths: example: https://fhir.nhs.uk/Id/nhs-number|9000000009 schema: type: string - + - in: query name: type description: General Practice Summary snomed code. Must be equal "type=http://snomed.info/sct|196981000000101" @@ -507,7 +505,7 @@ paths: example: http://snomed.info/sct|196981000000101 schema: type: string - + - in: query name: _sort description: Defines how Patient's SCR list should be sorted in order to retrieve the latest one. The only supported value is _sort=date. If a different value is provided 400 HTTP status will be returned. @@ -515,7 +513,7 @@ paths: example: date schema: type: string - + - in: query name: _count description: Defines the number of latest patient SCR IDs that could be retrieved. Currently the only supported value is _count=1. If a different value is provided 400 HTTP status will be returned. @@ -523,7 +521,7 @@ paths: example: 1 schema: type: integer - + responses: "200": description: Success response @@ -600,13 +598,13 @@ paths: | -------------------------------- | ---------------------------------------------------------------------------- | -------------------- | | Happy path | `NHSD-Session-URID`=`555254240100`; `nhsNumber`=`9000000009` | HTTP Status 201 | | Patient not found | `NHSD-Session-URID`=`555254240100`; `nhsNumber`=`9111231130` | HTTP Status 400 | - + parameters: - - $ref: '#/components/parameters/BearerAuthorization' - - $ref: '#/components/parameters/CorrelationID' - - $ref: '#/components/parameters/RequestID' - - $ref: '#/components/parameters/RoleID' - - $ref: '#/components/parameters/ContentType' + - $ref: "#/components/parameters/BearerAuthorization" + - $ref: "#/components/parameters/CorrelationID" + - $ref: "#/components/parameters/RequestID" + - $ref: "#/components/parameters/RoleID" + - $ref: "#/components/parameters/ContentType" requestBody: required: true content: @@ -615,8 +613,8 @@ paths: $ref: components/schemas/Parameters.yaml example: $ref: components/examples/SetPermission.json - responses: - "201": + responses: + "201": description: Permission successfully updated "4XX": description: | @@ -666,7 +664,7 @@ paths: description: | ## Overview Use this endpoint to raise an Electronic Alert to an organisation's Privacy Officer(s) when a Care Professional in the same organisation creates a Self-Claim LR or accesses an SCR without the patient's permission. - + The following combinations of type and subtype are possible. Note the non-permitted combinations in this list: | Requirement Identifier | Alert Type | Alert Type Display | Alert Sub-Type
(“Reason Code”) | Alert Sub-Type (“Reason Display”) | Business Scenario Notes | | ---------------------- | ---------- | ------------------------ | --------------------------------- | ---------------------------------- | ------------------------------------------ | @@ -682,20 +680,20 @@ paths: | n/a | 2 | n/a | 4 | n/a | Not a permitted combination | | MSCA-SCR-104 | 2 | Access Alert | 5 | Access made in an emergency | SCR accessed for emergency reasons. | | n/a | 2 | n/a | 6 | n/a | Not a permitted combination | - + ## Sandbox test scenarios You can test the following scenarios in our sandbox environment: | Scenario | Request | Response | | -------------------------------- | ---------------------------------------- | -------------------- | | Happy path | `nhsNumber`=`9000000009` | HTTP Status 201 | - + parameters: - - $ref: '#/components/parameters/BearerAuthorization' - - $ref: '#/components/parameters/CorrelationID' - - $ref: '#/components/parameters/RequestID' - - $ref: '#/components/parameters/RoleID' - - $ref: '#/components/parameters/ContentType' + - $ref: "#/components/parameters/BearerAuthorization" + - $ref: "#/components/parameters/CorrelationID" + - $ref: "#/components/parameters/RequestID" + - $ref: "#/components/parameters/RoleID" + - $ref: "#/components/parameters/ContentType" requestBody: required: true content: @@ -759,7 +757,7 @@ components: schema: type: string format: '^Bearer\ [[:ascii:]]+$' - example: 'Bearer g1112R_ccQ1Ebbb4gtHBP1aaaNM' + example: "Bearer g1112R_ccQ1Ebbb4gtHBP1aaaNM" CorrelationID: in: header name: X-Correlation-ID @@ -787,7 +785,7 @@ components: Mirrored back in a response header. schema: type: string - pattern: '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' + pattern: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" example: 60E0B220-8136-4CA5-AE46-1D97EF59D068 RoleID: @@ -802,8 +800,8 @@ components: required: false schema: type: string - pattern: '^[0-9]+$' - example: '555021935107' + pattern: "^[0-9]+$" + example: "555021935107" ContentType: in: header name: Content-Type @@ -812,4 +810,4 @@ components: required: true schema: type: string - example: 'application/fhir+json' + example: "application/fhir+json" From 92cdd4b9f40d5b38980a3699416451f4aa6484d1 Mon Sep 17 00:00:00 2001 From: stuart mcneill Date: Sun, 8 Jun 2025 22:38:09 +0100 Subject: [PATCH 03/13] fixed stubFailedSdsService test function --- .../uk/nhs/adaptors/scr/uat/SetAcsUAT.java | 101 +++++++++--------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java index 07091a34..0dc0da69 100644 --- a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java +++ b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java @@ -44,10 +44,10 @@ @SpringBootTest @AutoConfigureMockMvc -@ExtendWith({SpringExtension.class}) +@ExtendWith({ SpringExtension.class }) @DirtiesContext @Slf4j -@ContextConfiguration(initializers = {WireMockInitializer.class}) +@ContextConfiguration(initializers = { WireMockInitializer.class }) public class SetAcsUAT { private static final String SPINE_ACS_ENDPOINT = "/sync-service"; private static final String ACS_QUERY_HEADER = "urn:nhs:names:services:lrs/SET_RESOURCE_PERMISSIONS_INUK01"; @@ -87,7 +87,7 @@ public void testSetAcsPermissionViaSds(TestData testData) throws Exception { stubSdsService(practitionerRoleResponse); performRequest(testData.getFhirRequest()) - .andExpect(status().isCreated()); + .andExpect(status().isCreated()); } @ParameterizedTest(name = "[{index}] - {0}") @@ -97,7 +97,7 @@ public void testSetAcsPermissionViaUserInfo(TestData testData) throws Exception stubIdentityService(userInfoResponse); performRequest(testData.getFhirRequest()) - .andExpect(status().isCreated()); + .andExpect(status().isCreated()); } @ParameterizedTest(name = "[{index}] - {0}") @@ -107,8 +107,8 @@ public void testSetAcsPermissionSpineError(TestData testData) throws Exception { stubIdentityService(userInfoResponse); performRequest(testData.getFhirRequest()) - .andExpect(status().isBadRequest()) - .andExpect(content().json(testData.getFhirResponse())); + .andExpect(status().isBadRequest()) + .andExpect(content().json(testData.getFhirResponse())); } @ParameterizedTest(name = "[{index}] - {0}") @@ -117,83 +117,84 @@ public void testSetAcsPermissionBadRequest(TestData testData) throws Exception { stubIdentityService(userInfoResponse); performRequest(testData.getFhirRequest()) - .andExpect(status().isBadRequest()) - .andExpect(content().json(testData.getFhirResponse())); + .andExpect(status().isBadRequest()) + .andExpect(content().json(testData.getFhirResponse())); } @ParameterizedTest(name = "[{index}] - {0}") @ArgumentsSource(SetAcsBadRequest.class) public void testSetAcsPermissionNoRoleCodeBadRequest(TestData testData) throws Exception { - // FLAGSAPI-1046 should return Bad Request if no role code is returned from SDS or Identity Service + // FLAGSAPI-1046 should return Bad Request if no role code is returned from SDS + // or Identity Service stubFailedIdentityService(); stubFailedSdsService(); stubSpineAcsEndpoint(acsErrorResponse); performRequest(testData.getFhirRequest()) - .andExpect(status().isBadRequest()) - .andExpect(content().json(testData.getFhirResponse())); + .andExpect(status().isBadRequest()) + .andExpect(content().json(testData.getFhirResponse())); } private ResultActions performRequest(String request) throws Exception { return mockMvc.perform(post(ACS_ENDPOINT) - .contentType(APPLICATION_FHIR_JSON) - .header(ScrHttpHeaders.NHSD_ASID, NHSD_ASID) - .header(ScrHttpHeaders.CLIENT_IP, CLIENT_IP) - .header(ScrHttpHeaders.NHSD_SESSION_URID, NHSD_SESSION_URID) - .header(ScrHttpHeaders.NHSD_IDENTITY, NHSD_IDENTITY_UUID) - .header(AUTHORIZATION, BEARER_TOKEN) - .content(request)); + .contentType(APPLICATION_FHIR_JSON) + .header(ScrHttpHeaders.NHSD_ASID, NHSD_ASID) + .header(ScrHttpHeaders.CLIENT_IP, CLIENT_IP) + .header(ScrHttpHeaders.NHSD_SESSION_URID, NHSD_SESSION_URID) + .header(ScrHttpHeaders.NHSD_IDENTITY, NHSD_IDENTITY_UUID) + .header(AUTHORIZATION, BEARER_TOKEN) + .content(request)); } private void stubSpineAcsEndpoint(Resource response) throws IOException { wireMockServer.stubFor( - WireMock.post(SPINE_ACS_ENDPOINT) - .withHeader(SOAP_ACTION, equalTo(ACS_QUERY_HEADER)) - .withHeader(CONTENT_TYPE, equalTo(TEXT_XML_VALUE)) - .willReturn(aResponse() - .withStatus(OK.value()) - .withBody(readString(response.getFile().toPath(), UTF_8)))); + WireMock.post(SPINE_ACS_ENDPOINT) + .withHeader(SOAP_ACTION, equalTo(ACS_QUERY_HEADER)) + .withHeader(CONTENT_TYPE, equalTo(TEXT_XML_VALUE)) + .willReturn(aResponse() + .withStatus(OK.value()) + .withBody(readString(response.getFile().toPath(), UTF_8)))); } private void stubSdsService(Resource response) throws IOException { wireMockServer.stubFor( - WireMock.get(WireMock.urlPathEqualTo(PRACTITIONER_ROLE_ENDPOINT)) - .withQueryParam(USER_ID_QUERY_PARAM, - containing(NHSD_SESSION_URID)) - .willReturn(aResponse() - .withStatus(OK.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(readString(response.getFile().toPath(), UTF_8)))); + WireMock.get(WireMock.urlPathEqualTo(PRACTITIONER_ROLE_ENDPOINT)) + .withQueryParam(USER_ID_QUERY_PARAM, + containing(NHSD_SESSION_URID)) + .willReturn(aResponse() + .withStatus(OK.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withBody(readString(response.getFile().toPath(), UTF_8)))); } - private void stubFailedSdsService(Resource response) throws IOException { + private void stubFailedSdsService() { wireMockServer.stubFor( - WireMock.get(WireMock.urlPathEqualTo(PRACTITIONER_ROLE_ENDPOINT)) - .withQueryParam(USER_ID_QUERY_PARAM, - containing(NHSD_SESSION_URID)) - .willReturn(aResponse() - .withStatus(OK.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(readString(response.getFile().toPath(), UTF_8)))); + WireMock.get(WireMock.urlPathEqualTo(PRACTITIONER_ROLE_ENDPOINT)) + .withQueryParam(USER_ID_QUERY_PARAM, + containing(NHSD_SESSION_URID)) + .willReturn(aResponse() + .withStatus(OK.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withBody(""))); } private void stubIdentityService(Resource response) throws IOException { wireMockServer.stubFor( - WireMock.get(USER_INFO_ENDPOINT) - .withHeader(AUTHORIZATION, equalTo(BEARER_TOKEN)) - .willReturn(aResponse() - .withStatus(OK.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(readString(response.getFile().toPath(), UTF_8)))); + WireMock.get(USER_INFO_ENDPOINT) + .withHeader(AUTHORIZATION, equalTo(BEARER_TOKEN)) + .willReturn(aResponse() + .withStatus(OK.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withBody(readString(response.getFile().toPath(), UTF_8)))); } private void stubFailedIdentityService() { wireMockServer.stubFor( - WireMock.get(USER_INFO_ENDPOINT) - .withHeader(AUTHORIZATION, equalTo(BEARER_TOKEN)) - .willReturn(aResponse() - .withStatus(BAD_REQUEST.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE))); + WireMock.get(USER_INFO_ENDPOINT) + .withHeader(AUTHORIZATION, equalTo(BEARER_TOKEN)) + .willReturn(aResponse() + .withStatus(BAD_REQUEST.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE))); } } From 5edfc5b6cbf9ec628075f695039d8a383ec96ace Mon Sep 17 00:00:00 2001 From: stuart mcneill Date: Sun, 8 Jun 2025 22:52:38 +0100 Subject: [PATCH 04/13] FLAGSAPI-1046 reverted formatting changes --- .../uk/nhs/adaptors/scr/uat/GetScrIdUAT.java | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/GetScrIdUAT.java b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/GetScrIdUAT.java index b3b8b7a8..fedf49c1 100644 --- a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/GetScrIdUAT.java +++ b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/GetScrIdUAT.java @@ -43,10 +43,10 @@ @SpringBootTest @AutoConfigureMockMvc -@ExtendWith({ SpringExtension.class }) +@ExtendWith({SpringExtension.class}) @DirtiesContext @Slf4j -@ContextConfiguration(initializers = { WireMockInitializer.class }) +@ContextConfiguration(initializers = {WireMockInitializer.class}) public class GetScrIdUAT { private static final String EVENT_LIST_QUERY_HEADER = "urn:nhs:names:services:psisquery/QUPC_IN180000SM04"; @@ -57,11 +57,11 @@ public class GetScrIdUAT { private static final String TYPE_PARAM = "http://snomed.info/sct|196981000000101"; private static final String NHSD_ASID = "1029384756"; private static final String CLIENT_IP = "192.168.0.24"; - private static final String[] IGNORED_JSON_PATHS = new String[] { - "id", - "entry[*].fullUrl", - "entry[*].resource.subject.reference", - "entry[*].resource.id" + private static final String[] IGNORED_JSON_PATHS = new String[]{ + "id", + "entry[*].fullUrl", + "entry[*].resource.subject.reference", + "entry[*].resource.id" }; @Value("classpath:uat/responses/event-list-query/success.xml") @@ -113,24 +113,24 @@ void testRetrieveLatestScrIdNoConsent(TestData testData) throws Exception { private void performRequestAndAssert(TestData testData, HttpStatus expectedHttpStatus) throws Exception { mockMvc.perform(get(GET_SCR_ID_ENDPOINT) - .contentType(APPLICATION_FHIR_JSON_VALUE) - .header(ScrHttpHeaders.NHSD_ASID, NHSD_ASID) - .header(ScrHttpHeaders.CLIENT_IP, CLIENT_IP) - .queryParam("patient", NHS_NUMBER) - .queryParam("type", TYPE_PARAM) - .queryParam("_sort", SORT_PARAM) - .queryParam("_count", COUNT_PARAM)) - .andExpect(status().is(expectedHttpStatus.value())) - .andExpect(fhirJson(testData.getFhirResponse(), IGNORED_JSON_PATHS)); + .contentType(APPLICATION_FHIR_JSON_VALUE) + .header(ScrHttpHeaders.NHSD_ASID, NHSD_ASID) + .header(ScrHttpHeaders.CLIENT_IP, CLIENT_IP) + .queryParam("patient", NHS_NUMBER) + .queryParam("type", TYPE_PARAM) + .queryParam("_sort", SORT_PARAM) + .queryParam("_count", COUNT_PARAM)) + .andExpect(status().is(expectedHttpStatus.value())) + .andExpect(fhirJson(testData.getFhirResponse(), IGNORED_JSON_PATHS)); } private void stubSpinePsisEndpoint(Resource response) throws IOException { wireMockServer.stubFor( - WireMock.post(spineConfiguration.getPsisQueriesEndpoint()) - .withHeader(SOAP_ACTION, equalTo(EVENT_LIST_QUERY_HEADER)) - .withHeader(CONTENT_TYPE, equalTo(TEXT_XML_VALUE)) - .willReturn(aResponse() - .withStatus(OK.value()) - .withBody(readString(response.getFile().toPath(), UTF_8)))); + WireMock.post(spineConfiguration.getPsisQueriesEndpoint()) + .withHeader(SOAP_ACTION, equalTo(EVENT_LIST_QUERY_HEADER)) + .withHeader(CONTENT_TYPE, equalTo(TEXT_XML_VALUE)) + .willReturn(aResponse() + .withStatus(OK.value()) + .withBody(readString(response.getFile().toPath(), UTF_8)))); } } From 076b66937710cfe3473b9f2dea54172c9a081fdc Mon Sep 17 00:00:00 2001 From: stuart mcneill Date: Mon, 9 Jun 2025 10:47:31 +0100 Subject: [PATCH 05/13] FLAGSAPI-1046 reverted most of the the auto formatting changes to simplify PR --- docker/service/build.gradle | 6 + .../uk/nhs/adaptors/scr/uat/SetAcsUAT.java | 79 ++++---- .../adaptors/scr/services/GetScrService.java | 20 +- specification/summary-care-record.yaml | 180 +++++++++--------- 4 files changed, 146 insertions(+), 139 deletions(-) diff --git a/docker/service/build.gradle b/docker/service/build.gradle index 6261b98a..de672cbc 100644 --- a/docker/service/build.gradle +++ b/docker/service/build.gradle @@ -50,10 +50,16 @@ dependencies { testImplementation "io.rest-assured:json-path:4.4.0" testImplementation "io.rest-assured:xml-path:4.4.0" testImplementation "com.github.tomakehurst:wiremock-jre8-standalone:2.31.0" + testImplementation(platform('org.junit:junit-bom:5.13.0')) + testImplementation('org.junit.jupiter:junit-jupiter') + testRuntimeOnly('org.junit.platform:junit-platform-launcher') } test { useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } } sourceSets { diff --git a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java index 0dc0da69..5b251313 100644 --- a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java +++ b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java @@ -44,10 +44,10 @@ @SpringBootTest @AutoConfigureMockMvc -@ExtendWith({ SpringExtension.class }) +@ExtendWith({SpringExtension.class}) @DirtiesContext @Slf4j -@ContextConfiguration(initializers = { WireMockInitializer.class }) +@ContextConfiguration(initializers = {WireMockInitializer.class}) public class SetAcsUAT { private static final String SPINE_ACS_ENDPOINT = "/sync-service"; private static final String ACS_QUERY_HEADER = "urn:nhs:names:services:lrs/SET_RESOURCE_PERMISSIONS_INUK01"; @@ -87,7 +87,7 @@ public void testSetAcsPermissionViaSds(TestData testData) throws Exception { stubSdsService(practitionerRoleResponse); performRequest(testData.getFhirRequest()) - .andExpect(status().isCreated()); + .andExpect(status().isCreated()); } @ParameterizedTest(name = "[{index}] - {0}") @@ -97,7 +97,7 @@ public void testSetAcsPermissionViaUserInfo(TestData testData) throws Exception stubIdentityService(userInfoResponse); performRequest(testData.getFhirRequest()) - .andExpect(status().isCreated()); + .andExpect(status().isCreated()); } @ParameterizedTest(name = "[{index}] - {0}") @@ -107,8 +107,8 @@ public void testSetAcsPermissionSpineError(TestData testData) throws Exception { stubIdentityService(userInfoResponse); performRequest(testData.getFhirRequest()) - .andExpect(status().isBadRequest()) - .andExpect(content().json(testData.getFhirResponse())); + .andExpect(status().isBadRequest()) + .andExpect(content().json(testData.getFhirResponse())); } @ParameterizedTest(name = "[{index}] - {0}") @@ -117,8 +117,8 @@ public void testSetAcsPermissionBadRequest(TestData testData) throws Exception { stubIdentityService(userInfoResponse); performRequest(testData.getFhirRequest()) - .andExpect(status().isBadRequest()) - .andExpect(content().json(testData.getFhirResponse())); + .andExpect(status().isBadRequest()) + .andExpect(content().json(testData.getFhirResponse())); } @ParameterizedTest(name = "[{index}] - {0}") @@ -137,35 +137,34 @@ public void testSetAcsPermissionNoRoleCodeBadRequest(TestData testData) throws E private ResultActions performRequest(String request) throws Exception { return mockMvc.perform(post(ACS_ENDPOINT) - .contentType(APPLICATION_FHIR_JSON) - .header(ScrHttpHeaders.NHSD_ASID, NHSD_ASID) - .header(ScrHttpHeaders.CLIENT_IP, CLIENT_IP) - .header(ScrHttpHeaders.NHSD_SESSION_URID, NHSD_SESSION_URID) - .header(ScrHttpHeaders.NHSD_IDENTITY, NHSD_IDENTITY_UUID) - .header(AUTHORIZATION, BEARER_TOKEN) - .content(request)); + .contentType(APPLICATION_FHIR_JSON) + .header(ScrHttpHeaders.NHSD_ASID, NHSD_ASID) + .header(ScrHttpHeaders.CLIENT_IP, CLIENT_IP) + .header(ScrHttpHeaders.NHSD_SESSION_URID, NHSD_SESSION_URID) + .header(ScrHttpHeaders.NHSD_IDENTITY, NHSD_IDENTITY_UUID) + .header(AUTHORIZATION, BEARER_TOKEN) + .content(request)); } private void stubSpineAcsEndpoint(Resource response) throws IOException { wireMockServer.stubFor( - WireMock.post(SPINE_ACS_ENDPOINT) - .withHeader(SOAP_ACTION, equalTo(ACS_QUERY_HEADER)) - .withHeader(CONTENT_TYPE, equalTo(TEXT_XML_VALUE)) - .willReturn(aResponse() - .withStatus(OK.value()) - .withBody(readString(response.getFile().toPath(), UTF_8)))); + WireMock.post(SPINE_ACS_ENDPOINT) + .withHeader(SOAP_ACTION, equalTo(ACS_QUERY_HEADER)) + .withHeader(CONTENT_TYPE, equalTo(TEXT_XML_VALUE)) + .willReturn(aResponse() + .withStatus(OK.value()) + .withBody(readString(response.getFile().toPath(), UTF_8)))); } - private void stubSdsService(Resource response) throws IOException { + private void stubSpineAcsEndpoint(Resource response) throws IOException { wireMockServer.stubFor( - WireMock.get(WireMock.urlPathEqualTo(PRACTITIONER_ROLE_ENDPOINT)) - .withQueryParam(USER_ID_QUERY_PARAM, - containing(NHSD_SESSION_URID)) - .willReturn(aResponse() - .withStatus(OK.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(readString(response.getFile().toPath(), UTF_8)))); + WireMock.post(SPINE_ACS_ENDPOINT) + .withHeader(SOAP_ACTION, equalTo(ACS_QUERY_HEADER)) + .withHeader(CONTENT_TYPE, equalTo(TEXT_XML_VALUE)) + .willReturn(aResponse() + .withStatus(OK.value()) + .withBody(readString(response.getFile().toPath(), UTF_8)))); } private void stubFailedSdsService() { @@ -181,20 +180,20 @@ private void stubFailedSdsService() { private void stubIdentityService(Resource response) throws IOException { wireMockServer.stubFor( - WireMock.get(USER_INFO_ENDPOINT) - .withHeader(AUTHORIZATION, equalTo(BEARER_TOKEN)) - .willReturn(aResponse() - .withStatus(OK.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(readString(response.getFile().toPath(), UTF_8)))); + WireMock.get(USER_INFO_ENDPOINT) + .withHeader(AUTHORIZATION, equalTo(BEARER_TOKEN)) + .willReturn(aResponse() + .withStatus(OK.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withBody(readString(response.getFile().toPath(), UTF_8)))); } private void stubFailedIdentityService() { wireMockServer.stubFor( - WireMock.get(USER_INFO_ENDPOINT) - .withHeader(AUTHORIZATION, equalTo(BEARER_TOKEN)) - .willReturn(aResponse() - .withStatus(BAD_REQUEST.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE))); + WireMock.get(USER_INFO_ENDPOINT) + .withHeader(AUTHORIZATION, equalTo(BEARER_TOKEN)) + .willReturn(aResponse() + .withStatus(BAD_REQUEST.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE))); } } diff --git a/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java b/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java index 28fbed39..44f76a7a 100644 --- a/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java +++ b/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java @@ -133,11 +133,13 @@ public Bundle getScrId(String nhsNumber, String nhsdAsid, String clientIp) { bundle.addEntry(new BundleEntryComponent() .setFullUrl(getScrUrl() + "/DocumentReference/" + documentReference.getId()) .setResource(documentReference) - .setSearch(new Bundle.BundleEntrySearchComponent().setMode(MATCH))); + .setSearch(new Bundle.BundleEntrySearchComponent().setMode(MATCH)) + ); bundle.addEntry(new BundleEntryComponent() .setFullUrl(patient.getId()) - .setResource(patient)); + .setResource(patient) + ); } else { bundle.setTotal(0); } @@ -233,8 +235,8 @@ private void checkDetectedIssues(Document document) { } private DocumentReference buildDocumentReference(String nhsNumber, - EventListQueryResponse response, - Patient patient) { + EventListQueryResponse response, + Patient patient) { DocumentReference documentReference = new DocumentReference(); documentReference.setId(randomUUID()); @@ -246,7 +248,8 @@ private DocumentReference buildDocumentReference(String nhsNumber, documentReference.setType(GP_SUMMARY_SNOMED); documentReference.setSubject(new Reference(patient)); - DocumentReferenceContentComponent content = buildDocumentReferenceContent(nhsNumber, response.getLatestScrId()); + DocumentReferenceContentComponent content = + buildDocumentReferenceContent(nhsNumber, response.getLatestScrId()); documentReference.addContent(content); documentReference.setMasterIdentifier(new Identifier() @@ -267,6 +270,7 @@ private DocumentReferenceContentComponent buildDocumentReferenceContent(String n return content; } + private Patient buildPatientResource(String nhsNumber) { Patient patient = new Patient(); String patientResourceId = randomUUID(); @@ -281,8 +285,7 @@ private Patient buildPatientResource(String nhsNumber) { private String prepareEventListQueryRequest(String nhsNumber, String nhsdAsid, String clientIp) { EventListQueryParams eventListQueryParams = new EventListQueryParams() .setGeneratedMessageId(MDC.get(CORRELATION_ID_MDC_KEY)) - .setMessageCreationTime( - DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now(ZoneId.of("Europe/London")))) + .setMessageCreationTime(DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now(ZoneId.of("Europe/London")))) .setNhsNumber(nhsNumber) .setSenderFromASID(nhsdAsid) .setSpineToASID(scrConfiguration.getNhsdAsidTo()) @@ -294,8 +297,7 @@ private String prepareEventListQueryRequest(String nhsNumber, String nhsdAsid, S private String prepareEventQueryRequest(String psisEventId, String nhsNumber, String nhsdAsid, String clientIp) { var eventListQueryParams = new EventQueryParams() .setGeneratedMessageId(MDC.get(CORRELATION_ID_MDC_KEY)) - .setMessageCreationTime( - DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now(ZoneId.of("Europe/London")))) + .setMessageCreationTime(DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now(ZoneId.of("Europe/London")))) .setNhsNumber(nhsNumber) .setSenderFromASID(nhsdAsid) .setSpineToASID(scrConfiguration.getNhsdAsidTo()) diff --git a/specification/summary-care-record.yaml b/specification/summary-care-record.yaml index 0bba7f06..8a39d79c 100644 --- a/specification/summary-care-record.yaml +++ b/specification/summary-care-record.yaml @@ -3,7 +3,7 @@ info: version: 1.0.0 title: Summary Care Record API description: | - +
@@ -18,13 +18,13 @@ info:
- + ## Version The current released version of this API is 3.0 while minor updates may be available on PTL environments. ## Overview Use this API to access or update a patient's [Summary Care Record (SCR)](https://digital.nhs.uk/services/summary-care-records-scr) - an electronic record of important patient information, created from GP medical records. SCRs can be seen and used by authorised staff in other areas of the health and care system involved in the patient's direct care. - + This API is currently only approved for use in primary care software, specifically GP software. We hope to make it available for secondary care in the future. You can vote for this on our [interactive product backlog](https://nhs-digital-api-management.featureupvote.com/suggestions/151217/summary-care-record-scr-fhir-api-other-use-cases). Also use this API to update a patient's SCR Consent Preference on the Spine ACS and to raise electronic Alerts when: @@ -40,12 +40,12 @@ info: * upload a patient's SCR * send a privacy alert message, if you have to override a patient's dissent to view their SCR * update a patient's SCR Consent Preference on the Spine ACS - + A health or care staff providing direct care to patients must be present and authenticated with an [NHS smartcard or a modern alternative](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/nhs-smartcards-for-developers) to use this API. - + ## 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 do this before you can go live (see 'Onboarding' below). - + This API is initially for use by new market entrant GP IT developers with other use cases to follow later. ## Related APIs @@ -53,12 +53,12 @@ info: - [Personal Demographics Service (FHIR) API](https://digital.nhs.uk/developer/api-catalogue/personal-demographics-service-fhir) - use this API to search for patients and retrieve their details. This API can also be used to update their details in some cases. This is the latest version of the PDS API and is recommended for all new integrators. This API has endpoints enabling you to get and set access permissions, the same as the [Access Control Service (ACS) HL7 V3 API](https://digital.nhs.uk/developer/api-catalogue/access-control-service-hl7-v3) which it partly replaces. - + ## API status and roadmap This API is initially for use by new market entrant GP IT developers with other use cases to follow later. - + This API is [in production, beta](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#statuses), meaning: - + - the API is available in our sandbox and integration test environments - the API is available for production (private beta) use - we might make breaking changes, but only if we cannot avoid it, and we'll give advance notice @@ -73,7 +73,7 @@ info: It conforms to the [FHIR](https://digital.nhs.uk/developer/guides-and-documentation/our-api-technologies#fhir) global standard for health care data exchange, specifically to [FHIR R4 (v4.0.1)](https://hl7.org/fhir/r4/), except that it does not support the [capabilities](https://hl7.org/fhir/R4/http.html#capabilities) interaction. It includes some country-specific FHIR extensions, which are built against [FHIR UK Core](https://digital.nhs.uk/services/fhir-uk-core), specifically [UK.core.r4.v2 2.0.5](https://simplifier.net/packages/uk.core.r4.v2/2.0.5). - + The interactions between the SCR FHIR API and Spine have been tested with a wide range of special characters including emojis with appropriate Fitzpatrick modifiers. In accordance with HL7-FHIR specifications, this API operates with UTF-8 encoding. ## Network access @@ -94,13 +94,13 @@ info: For more details, see [user-restricted APIs](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation#user-restricted-apis). The second authorisation method is [application-restricted](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation#application-restricted-apis) (signed JWT authentication), meaning a few specific API calls can be authorised by the application making the requests. This is typically provided so that GPs can perform batch updates of multiple Summary Care Records without having to log in as a specific user. - + The following specific endpoint and method combinations can be used with application-restricted authentication, in addition to user-restricted authentication: - + - GET DocumentReference - GET Bundle - POST Bundle - + For more details, see: - [application-restricted APIs](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation#application-restricted-apis) and - [application-restricted RESTful APIs - signed with JWT authentication](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/application-restricted-restful-apis-signed-jwt-authentication) @@ -111,10 +111,10 @@ info: | Sandbox | `https://sandbox.api.service.nhs.uk/summary-care-record/FHIR/R4`| | Integration test | `https://int.api.service.nhs.uk/summary-care-record/FHIR/R4` | | Production | `https://api.service.nhs.uk/summary-care-record/FHIR/R4` | - + ## NME Testing The approach to testing for external developers is as follows. - + For sandbox, New Market Entrants (NMEs) should perform Self Testing, followed by Integration Testing which will be available on the Assurance Technical Evidencing. NMEs need to meet these to progress in integration. ### Sandbox testing @@ -143,18 +143,18 @@ info: * is for formal use in a live environment * is stateful, so persists updates * includes authorisation, with [smartcard](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/nhs-smartcards-for-developers) - + For more information about our Production Environment, contact the [National Service Desk](ssd.nationalservicedesk@nhs.net) ## 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/summary-care-record-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). - +
@@ -169,31 +169,31 @@ info:
- + ### Troubleshooting - + * After setting up your new application through the NHS Developer Portal and connecting it to the Summary Care Record API, you will need to ensure your application has a valid ASID custom attribute. ITOC support services should be able to ensure your ASID values are setup to use the [QUPC_IN190000UK04](https://data.developer.nhs.uk/dms/mim/4.2.00/Domains/PSIS%20Query/Document%20files/PSIS%20Query%20IM.htm#_Toc_Section_6.2) interaction. This interaction is for access to resources on Personal Spine Information Service (PSIS), the former name of Summary Care Record. If this isn't setup, you may encounter 403 Forbidden errors when accessing records. This can be checked by emailing [itoc.supportdesk@nhs.net](mailto:itoc.supportdesk@nhs.net). * ITOC should also be able to apply the GP summary v3 message set to the endpoints for you. - + ## Errors We use standard HTTP status codes to show whether an API request succeeded or not. They are usually in the range: * 200 to 299 if it succeeded, including code 202 if it was accepted by an API that needs to wait for further action * 400 to 499 if it failed because of a client error by your application * 500 to 599 if it failed because of an error on our server - + Errors specific to each API are shown in the Endpoints section, under Response. See our [reference guide](https://digital.nhs.uk/developer/guides-and-documentation/reference-guide#http-status-codes) for more on errors. - + ## Exponential Backoff In the event of a transitory error: - + * 429 - exceeded application rate limit (5 TPS per application) * 500 - internal server error * 503 - service unavailable * 504 - response timed out - + The following retries are recommended: - + * Attempt 1: 1 minute after first failure * Attempt 2: 30 minutes after attempt 1 * Attempt 3: 60 minutes after attempt 2 @@ -202,10 +202,10 @@ info: * Attempt 6: 8 hours after attempt 5 * Attempt 7: 16 hours after attempt 6 * If attempt 7 fails, then fail permanently. - + ## Support The National Integration Adaptors & APIs (NIA) Support team provide a support mailbox to support new market entrants integrating with this adaptor. To raise a support ticket please contact [niasupport@nhs.net](mailto:niasupport@nhs.net) - + ## Operations Here are the links to the possible SCR operations. These endpoints are for create and consume use cases for GP systems: * [Get patient's Summary Care Record](#get-/Bundle) @@ -218,7 +218,7 @@ info: name: MIT contact: name: Summary Care Record FHIR API Support - url: "https://digital.nhs.uk/developer/help-and-support" + url: 'https://digital.nhs.uk/developer/help-and-support' email: api.management@nhs.net x-nhsd-api-platform: meta: @@ -228,23 +228,23 @@ x-nhsd-api-platform: description: This is a generated template API pipeline_name_prefix: "Summary-Care-Record" servers: - - url: "https://sandbox.api.service.nhs.uk/summary-care-record/FHIR/R4" + - url: 'https://sandbox.api.service.nhs.uk/summary-care-record/FHIR/R4' description: Sandbox environment - - url: "https://int.api.service.nhs.uk/summary-care-record/FHIR/R4" + - url: 'https://int.api.service.nhs.uk/summary-care-record/FHIR/R4' description: Integration test environment. x-spec-publication: operation-order: - - operations: - - method: GET - path: /DocumentReference - - method: GET - path: /Bundle - - method: POST - path: /Bundle - - method: POST - path: /AuditEvent - - method: POST - path: /$setPermission + - operations: + - method: GET + path: /DocumentReference + - method: GET + path: /Bundle + - method: POST + path: /Bundle + - method: POST + path: /AuditEvent + - method: POST + path: /$setPermission paths: /Bundle: post: @@ -255,26 +255,26 @@ paths: To execute the upload you must provide patient's NHS number. In case of an update, before executing the request, latest UUID must be obtained using GET /DocumentReference endpoint and set inside the request body. - + ## Usage diagram ![POST SCR Diagram](https://raw.githubusercontent.com/NHSDigital/summary-care-record-api/various-documentation-changes/specification/components/resources/images/diagram-1.jpg) ## Sandbox test scenarios You can test the following scenarios in our sandbox environment: - + | Scenario | Request | Response | | ----------------------------------| ------------------------------------------------- | -------------------- | | Happy path | `Patient.identifier.value`=`9000000009` | HTTP Status 201 | | No patient's consent to store SCR | `Patient.identifier.value`=`9111231130` | HTTP Status 403 | - + summary: Upload patient's Summary Care Record operationId: upload-scr parameters: - - $ref: "#/components/parameters/BearerAuthorization" - - $ref: "#/components/parameters/CorrelationID" - - $ref: "#/components/parameters/RequestID" - - $ref: "#/components/parameters/RoleID" - - $ref: "#/components/parameters/ContentType" + - $ref: '#/components/parameters/BearerAuthorization' + - $ref: '#/components/parameters/CorrelationID' + - $ref: '#/components/parameters/RequestID' + - $ref: '#/components/parameters/RoleID' + - $ref: '#/components/parameters/ContentType' requestBody: required: true content: @@ -364,9 +364,9 @@ paths: You must specify the NHS number and the UUID of the latest Summary Care Record of this patient. This UUID should be obtained using the GET /DocumentReference endpoint. If you specify an out-of-date UUID, this endpoint returns a Bundle with 0 entries. - + ## Usage diagram - + ![GET SCR Diagram](https://raw.githubusercontent.com/NHSDigital/summary-care-record-api/various-documentation-changes/specification/components/resources/images/diagram-2.jpg) ## Sandbox test scenarios @@ -377,13 +377,13 @@ paths: | Happy path | `composition.identifier`=`FA60BE64-1F34-11EB-A2A8-000C29A364EB` `composition.subject:Patient.identifier`=`9000000009` | HTTP Status 200 - Full Bundle | | No results | `composition.identifier`=`81CC2DA0-8882-11EB-B538-0800200C9A66` `composition.subject:Patient.identifier`=`9000000033` | HTTP Status 200 - Empty Bundle | | Invalid results | `composition.identifier`=`INVALID ID` `composition.subject:Patient.identifier`=`INVALID NHS NUMBER` | HTTP Status 400 - Invalid Request | - + summary: Get patient's Summary Care Record operationId: get-scr parameters: - - $ref: "#/components/parameters/BearerAuthorization" - - $ref: "#/components/parameters/CorrelationID" - - $ref: "#/components/parameters/RequestID" + - $ref: '#/components/parameters/BearerAuthorization' + - $ref: '#/components/parameters/CorrelationID' + - $ref: '#/components/parameters/RequestID' - in: query name: composition.identifier description: Latest Patient's Summary Care Record identifier. Can be obtained using GET /DocumentReference endpoint. @@ -470,7 +470,7 @@ paths: Use this endpoint to retrieve UUID of patient's latest record. The UUID is required to retrieve the Summary Care Record details using GET /Bundle endpoint and to update the details of patient's Summary Care Record using POST /Bundle endpoint. - + This endpoint also returns patient's consent status in the securityLabel attribute of a successful API response. To get the information you must provide patient's NHS number. @@ -478,18 +478,18 @@ paths: ## Sandbox test scenarios You can test the following scenarios in our sandbox environment: - + | Scenario | Request | Response | | ----------------------------------| --------------------------------- | ---------------------------------- | | Happy path | `nhs-number=9000000009` | HTTP Status 200 - Full Bundle | | Empty result | `nhs-number=9000000033` | HTTP Status 200 - Empty Bundle | | Invalid results | `nhs-number=INVALID NHS NUMBER` | HTTP Status 400 - Invalid Request | - + parameters: - - $ref: "#/components/parameters/BearerAuthorization" - - $ref: "#/components/parameters/CorrelationID" - - $ref: "#/components/parameters/RequestID" - - $ref: "#/components/parameters/RoleID" + - $ref: '#/components/parameters/BearerAuthorization' + - $ref: '#/components/parameters/CorrelationID' + - $ref: '#/components/parameters/RequestID' + - $ref: '#/components/parameters/RoleID' - in: query name: patient description: The patient's NHS number. Must be preceded with FHIR identifier (eg."patient=https://fhir.nhs.uk/Id/nhs-number|9000000009") @@ -497,7 +497,7 @@ paths: example: https://fhir.nhs.uk/Id/nhs-number|9000000009 schema: type: string - + - in: query name: type description: General Practice Summary snomed code. Must be equal "type=http://snomed.info/sct|196981000000101" @@ -505,7 +505,7 @@ paths: example: http://snomed.info/sct|196981000000101 schema: type: string - + - in: query name: _sort description: Defines how Patient's SCR list should be sorted in order to retrieve the latest one. The only supported value is _sort=date. If a different value is provided 400 HTTP status will be returned. @@ -513,7 +513,7 @@ paths: example: date schema: type: string - + - in: query name: _count description: Defines the number of latest patient SCR IDs that could be retrieved. Currently the only supported value is _count=1. If a different value is provided 400 HTTP status will be returned. @@ -521,7 +521,7 @@ paths: example: 1 schema: type: integer - + responses: "200": description: Success response @@ -598,13 +598,13 @@ paths: | -------------------------------- | ---------------------------------------------------------------------------- | -------------------- | | Happy path | `NHSD-Session-URID`=`555254240100`; `nhsNumber`=`9000000009` | HTTP Status 201 | | Patient not found | `NHSD-Session-URID`=`555254240100`; `nhsNumber`=`9111231130` | HTTP Status 400 | - + parameters: - - $ref: "#/components/parameters/BearerAuthorization" - - $ref: "#/components/parameters/CorrelationID" - - $ref: "#/components/parameters/RequestID" - - $ref: "#/components/parameters/RoleID" - - $ref: "#/components/parameters/ContentType" + - $ref: '#/components/parameters/BearerAuthorization' + - $ref: '#/components/parameters/CorrelationID' + - $ref: '#/components/parameters/RequestID' + - $ref: '#/components/parameters/RoleID' + - $ref: '#/components/parameters/ContentType' requestBody: required: true content: @@ -613,8 +613,8 @@ paths: $ref: components/schemas/Parameters.yaml example: $ref: components/examples/SetPermission.json - responses: - "201": + responses: + "201": description: Permission successfully updated "4XX": description: | @@ -664,7 +664,7 @@ paths: description: | ## Overview Use this endpoint to raise an Electronic Alert to an organisation's Privacy Officer(s) when a Care Professional in the same organisation creates a Self-Claim LR or accesses an SCR without the patient's permission. - + The following combinations of type and subtype are possible. Note the non-permitted combinations in this list: | Requirement Identifier | Alert Type | Alert Type Display | Alert Sub-Type
(“Reason Code”) | Alert Sub-Type (“Reason Display”) | Business Scenario Notes | | ---------------------- | ---------- | ------------------------ | --------------------------------- | ---------------------------------- | ------------------------------------------ | @@ -680,20 +680,20 @@ paths: | n/a | 2 | n/a | 4 | n/a | Not a permitted combination | | MSCA-SCR-104 | 2 | Access Alert | 5 | Access made in an emergency | SCR accessed for emergency reasons. | | n/a | 2 | n/a | 6 | n/a | Not a permitted combination | - + ## Sandbox test scenarios You can test the following scenarios in our sandbox environment: | Scenario | Request | Response | | -------------------------------- | ---------------------------------------- | -------------------- | | Happy path | `nhsNumber`=`9000000009` | HTTP Status 201 | - + parameters: - - $ref: "#/components/parameters/BearerAuthorization" - - $ref: "#/components/parameters/CorrelationID" - - $ref: "#/components/parameters/RequestID" - - $ref: "#/components/parameters/RoleID" - - $ref: "#/components/parameters/ContentType" + - $ref: '#/components/parameters/BearerAuthorization' + - $ref: '#/components/parameters/CorrelationID' + - $ref: '#/components/parameters/RequestID' + - $ref: '#/components/parameters/RoleID' + - $ref: '#/components/parameters/ContentType' requestBody: required: true content: @@ -757,7 +757,7 @@ components: schema: type: string format: '^Bearer\ [[:ascii:]]+$' - example: "Bearer g1112R_ccQ1Ebbb4gtHBP1aaaNM" + example: 'Bearer g1112R_ccQ1Ebbb4gtHBP1aaaNM' CorrelationID: in: header name: X-Correlation-ID @@ -785,7 +785,7 @@ components: Mirrored back in a response header. schema: type: string - pattern: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" + pattern: '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' example: 60E0B220-8136-4CA5-AE46-1D97EF59D068 RoleID: @@ -800,8 +800,8 @@ components: required: false schema: type: string - pattern: "^[0-9]+$" - example: "555021935107" + pattern: '^[0-9]+$' + example: '555021935107' ContentType: in: header name: Content-Type @@ -810,4 +810,4 @@ components: required: true schema: type: string - example: "application/fhir+json" + example: 'application/fhir+json' From 2371ba781884a78f5bf0f9fef2876de0828279a6 Mon Sep 17 00:00:00 2001 From: Gareth Somerville Date: Mon, 9 Jun 2025 10:54:38 +0100 Subject: [PATCH 06/13] Revert one more whitespace change --- .../java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java index 5b251313..8e42d99d 100644 --- a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java +++ b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java @@ -169,13 +169,13 @@ private void stubSpineAcsEndpoint(Resource response) throws IOException { private void stubFailedSdsService() { wireMockServer.stubFor( - WireMock.get(WireMock.urlPathEqualTo(PRACTITIONER_ROLE_ENDPOINT)) - .withQueryParam(USER_ID_QUERY_PARAM, - containing(NHSD_SESSION_URID)) - .willReturn(aResponse() - .withStatus(OK.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(""))); + WireMock.get(WireMock.urlPathEqualTo(PRACTITIONER_ROLE_ENDPOINT)) + .withQueryParam(USER_ID_QUERY_PARAM, + containing(NHSD_SESSION_URID)) + .willReturn(aResponse() + .withStatus(OK.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withBody(""))); } private void stubIdentityService(Resource response) throws IOException { From 4dd42796da09f5b4d66c7a6d415bae6931a069eb Mon Sep 17 00:00:00 2001 From: stuart mcneill Date: Mon, 9 Jun 2025 11:12:35 +0100 Subject: [PATCH 07/13] FLAGSAPI-1046 corrected test method name --- .../java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java index 5b251313..00d03fc9 100644 --- a/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java +++ b/docker/service/src/integration-test/java/uk/nhs/adaptors/scr/uat/SetAcsUAT.java @@ -157,13 +157,14 @@ private void stubSpineAcsEndpoint(Resource response) throws IOException { .withBody(readString(response.getFile().toPath(), UTF_8)))); } - private void stubSpineAcsEndpoint(Resource response) throws IOException { + private void stubSdsService(Resource response) throws IOException { wireMockServer.stubFor( - WireMock.post(SPINE_ACS_ENDPOINT) - .withHeader(SOAP_ACTION, equalTo(ACS_QUERY_HEADER)) - .withHeader(CONTENT_TYPE, equalTo(TEXT_XML_VALUE)) + WireMock.get(WireMock.urlPathEqualTo(PRACTITIONER_ROLE_ENDPOINT)) + .withQueryParam(USER_ID_QUERY_PARAM, + containing(NHSD_SESSION_URID)) .willReturn(aResponse() .withStatus(OK.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) .withBody(readString(response.getFile().toPath(), UTF_8)))); } From 9dace4104480690c35e05e5155c874d0c6cf304d Mon Sep 17 00:00:00 2001 From: stuart mcneill Date: Mon, 9 Jun 2025 11:25:23 +0100 Subject: [PATCH 08/13] FLAGSAPI-1046 reverted gitignore file --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index bc940fd0..d126e2f8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,3 @@ __pycache__/ .venv/ smoketest-report.xml *.code-workspace -.python-version - From d2bce6887d4196aefdf01b1e02b1b259f3e608f4 Mon Sep 17 00:00:00 2001 From: stuart mcneill Date: Mon, 16 Jun 2025 09:32:28 +0100 Subject: [PATCH 09/13] FLAGSAPI-1046 reverted change to ATTACHMENT_URL in GetScrService.java --- .../main/java/uk/nhs/adaptors/scr/services/GetScrService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java b/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java index 44f76a7a..6fcad968 100644 --- a/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java +++ b/docker/service/src/main/java/uk/nhs/adaptors/scr/services/GetScrService.java @@ -80,7 +80,7 @@ public class GetScrService { private static final String SNOMED_SYSTEM = "http://snomed.info/sct"; private static final String GP_SUMMARY_SNOMED_CODE = "196981000000101"; private static final String GP_SUMMARY_DISPLAY = " General Practice Summary"; - private static final String ATTACHMENT_URL = "%sBundle?composition.identifier=%s" + private static final String ATTACHMENT_URL = "%s/Bundle?composition.identifier=%s" + "&composition.subject:Patient.identifier=https://fhir.nhs.uk/Id/nhs-number|%s"; private static final CodeableConcept GP_SUMMARY_SNOMED = new CodeableConcept(new Coding() From aec8bc5af9ebdb63e17d232312e1b04ba453f7c2 Mon Sep 17 00:00:00 2001 From: stuart mcneill Date: Mon, 16 Jun 2025 21:21:03 +0100 Subject: [PATCH 10/13] FLAGSAPI-1046 simplified code by pre building an error string --- .../uk/nhs/adaptors/scr/services/AcsService.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docker/service/src/main/java/uk/nhs/adaptors/scr/services/AcsService.java b/docker/service/src/main/java/uk/nhs/adaptors/scr/services/AcsService.java index 3726bdb9..01bf80cf 100644 --- a/docker/service/src/main/java/uk/nhs/adaptors/scr/services/AcsService.java +++ b/docker/service/src/main/java/uk/nhs/adaptors/scr/services/AcsService.java @@ -90,6 +90,9 @@ private Pair getUserRoleCodeAndId(String authorisation, String n } + String errorMessage = String.format("Unable to determine Job Role Code for " + + "the given RoleID via the SDS Service: %s", nhsdSessionUrid); + // if we haven't retrieved a role code from openID we try the SDS service try { var roleCode = sdsService.getUserRoleCode(nhsdSessionUrid); @@ -97,15 +100,11 @@ private Pair getUserRoleCodeAndId(String authorisation, String n return Pair.of(roleCode, nhsdIdentity); } } catch (BadRequestException | URISyntaxException e) { - LOGGER.info(String.format("Unable to determine Job Role Code for " - + "the given RoleID via the SDS Service: %s", nhsdSessionUrid)); - throw new BadRequestException(String.format("Unable to determine SDS Job Role Code for " - + "the given RoleID: %s", nhsdSessionUrid)); + LOGGER.info(errorMessage); + throw new BadRequestException(errorMessage); } - LOGGER.info(String.format("Unable to determine Job Role Code for " - + "the given RoleID via the SDS Service: %s", nhsdSessionUrid)); - throw new BadRequestException(String.format("Unable to determine SDS Job Role Code for " - + "the given RoleID: %s", nhsdSessionUrid)); + LOGGER.info(errorMessage); + throw new BadRequestException(errorMessage); } private String prepareAcsRequest(ParametersParameterComponent parameter, RequestData requestData, String sdsJobRoleCode, From 9dd3cb91854793cb9fd9f782f09d5fdc30b538de Mon Sep 17 00:00:00 2001 From: stuart mcneill Date: Mon, 16 Jun 2025 21:22:08 +0100 Subject: [PATCH 11/13] FLAGSAPI-1046 fixed pipeline build python version error by specifying python 3.9 in run-tests yaml files --- azure/templates/run-tests-int.yml | 118 +++++++++++++++-------------- azure/templates/run-tests-prod.yml | 94 ++++++++++++----------- azure/templates/run-tests.yml | 118 +++++++++++++++-------------- 3 files changed, 171 insertions(+), 159 deletions(-) diff --git a/azure/templates/run-tests-int.yml b/azure/templates/run-tests-int.yml index 72b9e0db..d99afee2 100644 --- a/azure/templates/run-tests-int.yml +++ b/azure/templates/run-tests-int.yml @@ -1,63 +1,67 @@ parameters: - - name: smoke_tests - type: boolean - displayName: Toggle smoke tests - default: false + - name: smoke_tests + type: boolean + displayName: Toggle smoke tests + default: false steps: - - task: s3-cache-action@1 - inputs: - key: 'poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock' - location: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv' - debug: true - alias: 'Pytest' - displayName: cache pytest dependencies + - task: UsePythonVersion@0 + inputs: + versionSpec: "3.9" + + - task: s3-cache-action@1 + inputs: + key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock" + location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv" + debug: true + alias: "Pytest" + displayName: cache pytest dependencies - - bash: | - make install-python - workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - condition: ne(variables['CacheRestored-Pytest'], 'true') - displayName: Setup pytests - # Smoketests - - ${{ if parameters.smoke_tests }}: - - template: "azure/components/aws-assume-role.yml@common" - parameters: - role: "auto-ops" - profile: "apm_ptl" - - template: "azure/components/get-aws-secrets-and-ssm-params.yml@common" - parameters: - secret_file_ids: - - ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY - - ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY - config_ids: - - /ptl/azure-devops/summary-care-record/int/CLIENT_ID_INT - secret_ids: - - ptl/azure-devops/summary-care-record/int/CLIENT_SECRET_INT - bash: | - wait - sleep 350 - export RELEASE_RELEASEID=$(Build.BuildId) - export SOURCE_COMMIT_ID=$(Build.SourceVersion) - export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" - export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" - export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" - export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" - export OAUTH_PROXY="oauth2" - export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" - export APIGEE_API_TOKEN="$(secret.AccessToken)" - export REDIRECT_URI="https://example.org/callback" - export CLIENT_ID="$(CLIENT_ID_INT)" - export CLIENT_SECRET="$(CLIENT_SECRET_INT)" - export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" - export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" - export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="" - export AUTHENTICATE_URL="" - make prod-smoketest + make install-python workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - displayName: Run smoketests - - task: PublishTestResults@2 - displayName: 'Publish smoketest results' - condition: always() - inputs: - testResultsFiles: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml' - failTaskOnFailedTests: true + condition: ne(variables['CacheRestored-Pytest'], 'true') + displayName: Setup pytests + # Smoketests + - ${{ if parameters.smoke_tests }}: + - template: "azure/components/aws-assume-role.yml@common" + parameters: + role: "auto-ops" + profile: "apm_ptl" + - template: "azure/components/get-aws-secrets-and-ssm-params.yml@common" + parameters: + secret_file_ids: + - ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY + - ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY + config_ids: + - /ptl/azure-devops/summary-care-record/int/CLIENT_ID_INT + secret_ids: + - ptl/azure-devops/summary-care-record/int/CLIENT_SECRET_INT + - bash: | + wait + sleep 350 + export RELEASE_RELEASEID=$(Build.BuildId) + export SOURCE_COMMIT_ID=$(Build.SourceVersion) + export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" + export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" + export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" + export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" + export OAUTH_PROXY="oauth2" + export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" + export APIGEE_API_TOKEN="$(secret.AccessToken)" + export REDIRECT_URI="https://example.org/callback" + export CLIENT_ID="$(CLIENT_ID_INT)" + export CLIENT_SECRET="$(CLIENT_SECRET_INT)" + export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" + export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" + export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="" + export AUTHENTICATE_URL="" + make prod-smoketest + workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) + displayName: Run smoketests + - task: PublishTestResults@2 + displayName: "Publish smoketest results" + condition: always() + inputs: + testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml" + failTaskOnFailedTests: true diff --git a/azure/templates/run-tests-prod.yml b/azure/templates/run-tests-prod.yml index 6f57d6ee..9f2882de 100644 --- a/azure/templates/run-tests-prod.yml +++ b/azure/templates/run-tests-prod.yml @@ -1,48 +1,52 @@ steps: - - task: s3-cache-action@1 - inputs: - key: 'poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock' - location: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv' - debug: true - alias: 'Pytest' - displayName: cache pytest dependencies + - task: UsePythonVersion@0 + inputs: + versionSpec: "3.9" - - bash: | - make install-python - workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - condition: ne(variables['CacheRestored-Pytest'], 'true') - displayName: Setup pytests - # Smoketests - - template: "azure/components/aws-assume-role.yml@common" - parameters: - role: "auto-ops" - profile: "apm_ptl" - - bash: | - wait - sleep 350 - export RELEASE_RELEASEID=$(Build.BuildId) - export SOURCE_COMMIT_ID=$(Build.SourceVersion) - export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" - export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" - export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" - export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" - export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="" - export AUTHENTICATE_URL="" - export OAUTH_PROXY="oauth2" - export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" - export APIGEE_API_TOKEN="$(secret.AccessToken)" - export REDIRECT_URI="https://example.org/callback" - export CLIENT_ID="$(CLIENT_ID_INT)" - export CLIENT_SECRET="$(CLIENT_SECRET_INT)" - export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" - export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" + - task: s3-cache-action@1 + inputs: + key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock" + location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv" + debug: true + alias: "Pytest" + displayName: cache pytest dependencies - make prod-smoketest - workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - displayName: Run smoketests - - task: PublishTestResults@2 - displayName: 'Publish smoketest results' - condition: always() - inputs: - testResultsFiles: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml' - failTaskOnFailedTests: true + - bash: | + make install-python + workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) + condition: ne(variables['CacheRestored-Pytest'], 'true') + displayName: Setup pytests + # Smoketests + - template: "azure/components/aws-assume-role.yml@common" + parameters: + role: "auto-ops" + profile: "apm_ptl" + - bash: | + wait + sleep 350 + export RELEASE_RELEASEID=$(Build.BuildId) + export SOURCE_COMMIT_ID=$(Build.SourceVersion) + export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" + export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" + export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" + export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" + export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="" + export AUTHENTICATE_URL="" + export OAUTH_PROXY="oauth2" + export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" + export APIGEE_API_TOKEN="$(secret.AccessToken)" + export REDIRECT_URI="https://example.org/callback" + export CLIENT_ID="$(CLIENT_ID_INT)" + export CLIENT_SECRET="$(CLIENT_SECRET_INT)" + export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" + export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" + + make prod-smoketest + workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) + displayName: Run smoketests + - task: PublishTestResults@2 + displayName: "Publish smoketest results" + condition: always() + inputs: + testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml" + failTaskOnFailedTests: true diff --git a/azure/templates/run-tests.yml b/azure/templates/run-tests.yml index 699a2e67..0d25fba1 100644 --- a/azure/templates/run-tests.yml +++ b/azure/templates/run-tests.yml @@ -1,63 +1,67 @@ parameters: - - name: smoke_tests - type: boolean - displayName: Toggle smoke tests - default: false + - name: smoke_tests + type: boolean + displayName: Toggle smoke tests + default: false steps: - - task: s3-cache-action@1 - inputs: - key: 'poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock' - location: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv' - debug: true - alias: 'Pytest' - displayName: cache pytest dependencies + - task: UsePythonVersion@0 + inputs: + versionSpec: "3.9" + + - task: s3-cache-action@1 + inputs: + key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock" + location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv" + debug: true + alias: "Pytest" + displayName: cache pytest dependencies - - bash: | - make install-python - workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - condition: ne(variables['CacheRestored-Pytest'], 'true') - displayName: Setup pytests - # Smoketests. - - ${{ if parameters.smoke_tests }}: - - template: "azure/components/aws-assume-role.yml@common" - parameters: - role: "auto-ops" - profile: "apm_ptl" - - template: "azure/components/get-aws-secrets-and-ssm-params.yml@common" - parameters: - secret_file_ids: - - ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY - - ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY - - ptl/azure-devops/summary-care-record/JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY - config_ids: - - /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_ID - - /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_SECRET - bash: | - wait - sleep 350 - export RELEASE_RELEASEID=$(Build.BuildId) - export SOURCE_COMMIT_ID=$(Build.SourceVersion) - export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" - export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" - export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" - export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" - export OAUTH_PROXY="oauth2-mock" - export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" - export APIGEE_API_TOKEN="$(secret.AccessToken)" - export REDIRECT_URI="https://example.org/callback" - export CLIENT_ID="$(CLIENT_ID)" - export CLIENT_SECRET="$(CLIENT_SECRET)" - export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" - export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY)" - export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" - export AUTHENTICATE_URL="https://nhsd-apim-testing-internal-dev.herokuapp.com/" - make smoketest + make install-python workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - displayName: Run smoketests - - task: PublishTestResults@2 - displayName: 'Publish smoketest results' - condition: always() - inputs: - testResultsFiles: '$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml' - failTaskOnFailedTests: true + condition: ne(variables['CacheRestored-Pytest'], 'true') + displayName: Setup pytests + # Smoketests. + - ${{ if parameters.smoke_tests }}: + - template: "azure/components/aws-assume-role.yml@common" + parameters: + role: "auto-ops" + profile: "apm_ptl" + - template: "azure/components/get-aws-secrets-and-ssm-params.yml@common" + parameters: + secret_file_ids: + - ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY + - ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY + - ptl/azure-devops/summary-care-record/JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY + config_ids: + - /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_ID + - /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_SECRET + - bash: | + wait + sleep 350 + export RELEASE_RELEASEID=$(Build.BuildId) + export SOURCE_COMMIT_ID=$(Build.SourceVersion) + export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" + export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" + export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" + export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" + export OAUTH_PROXY="oauth2-mock" + export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" + export APIGEE_API_TOKEN="$(secret.AccessToken)" + export REDIRECT_URI="https://example.org/callback" + export CLIENT_ID="$(CLIENT_ID)" + export CLIENT_SECRET="$(CLIENT_SECRET)" + export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" + export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY)" + export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" + export AUTHENTICATE_URL="https://nhsd-apim-testing-internal-dev.herokuapp.com/" + make smoketest + workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) + displayName: Run smoketests + - task: PublishTestResults@2 + displayName: "Publish smoketest results" + condition: always() + inputs: + testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml" + failTaskOnFailedTests: true From 876218b2380f0cb1f6cdb2fe37f2824396132668 Mon Sep 17 00:00:00 2001 From: Gareth Somerville Date: Tue, 17 Jun 2025 10:25:30 +0100 Subject: [PATCH 12/13] Fix editorconfig --- .editorconfig | 2 +- azure/templates/run-tests-int.yml | 124 ++++++++++++++--------------- azure/templates/run-tests-prod.yml | 96 +++++++++++----------- azure/templates/run-tests.yml | 124 ++++++++++++++--------------- 4 files changed, 173 insertions(+), 173 deletions(-) diff --git a/.editorconfig b/.editorconfig index fb31d3e3..1107fe78 100755 --- a/.editorconfig +++ b/.editorconfig @@ -11,7 +11,7 @@ end_of_line = lf [Makefile] indent_style = tab -[*.{xml,js,json,yaml}] +[*.{xml,js,json,yaml,yml}] indent_size = 2 [*.postman_collection.json] diff --git a/azure/templates/run-tests-int.yml b/azure/templates/run-tests-int.yml index d99afee2..5a608940 100644 --- a/azure/templates/run-tests-int.yml +++ b/azure/templates/run-tests-int.yml @@ -1,67 +1,67 @@ parameters: - - name: smoke_tests - type: boolean - displayName: Toggle smoke tests - default: false + - name: smoke_tests + type: boolean + displayName: Toggle smoke tests + default: false steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: "3.9" + - task: UsePythonVersion@0 + inputs: + versionSpec: "3.9" - - task: s3-cache-action@1 - inputs: - key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock" - location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv" - debug: true - alias: "Pytest" - displayName: cache pytest dependencies + - task: s3-cache-action@1 + inputs: + key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock" + location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv" + debug: true + alias: "Pytest" + displayName: cache pytest dependencies - - bash: | - make install-python - workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - condition: ne(variables['CacheRestored-Pytest'], 'true') - displayName: Setup pytests - # Smoketests - - ${{ if parameters.smoke_tests }}: - - template: "azure/components/aws-assume-role.yml@common" - parameters: - role: "auto-ops" - profile: "apm_ptl" - - template: "azure/components/get-aws-secrets-and-ssm-params.yml@common" - parameters: - secret_file_ids: - - ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY - - ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY - config_ids: - - /ptl/azure-devops/summary-care-record/int/CLIENT_ID_INT - secret_ids: - - ptl/azure-devops/summary-care-record/int/CLIENT_SECRET_INT - - bash: | - wait - sleep 350 - export RELEASE_RELEASEID=$(Build.BuildId) - export SOURCE_COMMIT_ID=$(Build.SourceVersion) - export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" - export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" - export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" - export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" - export OAUTH_PROXY="oauth2" - export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" - export APIGEE_API_TOKEN="$(secret.AccessToken)" - export REDIRECT_URI="https://example.org/callback" - export CLIENT_ID="$(CLIENT_ID_INT)" - export CLIENT_SECRET="$(CLIENT_SECRET_INT)" - export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" - export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" - export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="" - export AUTHENTICATE_URL="" - make prod-smoketest - workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - displayName: Run smoketests - - task: PublishTestResults@2 - displayName: "Publish smoketest results" - condition: always() - inputs: - testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml" - failTaskOnFailedTests: true + - bash: | + make install-python + workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) + condition: ne(variables['CacheRestored-Pytest'], 'true') + displayName: Setup pytests + # Smoketests + - ${{ if parameters.smoke_tests }}: + - template: "azure/components/aws-assume-role.yml@common" + parameters: + role: "auto-ops" + profile: "apm_ptl" + - template: "azure/components/get-aws-secrets-and-ssm-params.yml@common" + parameters: + secret_file_ids: + - ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY + - ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY + config_ids: + - /ptl/azure-devops/summary-care-record/int/CLIENT_ID_INT + secret_ids: + - ptl/azure-devops/summary-care-record/int/CLIENT_SECRET_INT + - bash: | + wait + sleep 350 + export RELEASE_RELEASEID=$(Build.BuildId) + export SOURCE_COMMIT_ID=$(Build.SourceVersion) + export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" + export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" + export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" + export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" + export OAUTH_PROXY="oauth2" + export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" + export APIGEE_API_TOKEN="$(secret.AccessToken)" + export REDIRECT_URI="https://example.org/callback" + export CLIENT_ID="$(CLIENT_ID_INT)" + export CLIENT_SECRET="$(CLIENT_SECRET_INT)" + export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" + export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" + export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="" + export AUTHENTICATE_URL="" + make prod-smoketest + workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) + displayName: Run smoketests + - task: PublishTestResults@2 + displayName: "Publish smoketest results" + condition: always() + inputs: + testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml" + failTaskOnFailedTests: true diff --git a/azure/templates/run-tests-prod.yml b/azure/templates/run-tests-prod.yml index 9f2882de..9baa06e1 100644 --- a/azure/templates/run-tests-prod.yml +++ b/azure/templates/run-tests-prod.yml @@ -1,52 +1,52 @@ steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: "3.9" + - task: UsePythonVersion@0 + inputs: + versionSpec: "3.9" - - task: s3-cache-action@1 - inputs: - key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock" - location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv" - debug: true - alias: "Pytest" - displayName: cache pytest dependencies + - task: s3-cache-action@1 + inputs: + key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock" + location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv" + debug: true + alias: "Pytest" + displayName: cache pytest dependencies - - bash: | - make install-python - workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - condition: ne(variables['CacheRestored-Pytest'], 'true') - displayName: Setup pytests - # Smoketests - - template: "azure/components/aws-assume-role.yml@common" - parameters: - role: "auto-ops" - profile: "apm_ptl" - - bash: | - wait - sleep 350 - export RELEASE_RELEASEID=$(Build.BuildId) - export SOURCE_COMMIT_ID=$(Build.SourceVersion) - export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" - export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" - export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" - export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" - export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="" - export AUTHENTICATE_URL="" - export OAUTH_PROXY="oauth2" - export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" - export APIGEE_API_TOKEN="$(secret.AccessToken)" - export REDIRECT_URI="https://example.org/callback" - export CLIENT_ID="$(CLIENT_ID_INT)" - export CLIENT_SECRET="$(CLIENT_SECRET_INT)" - export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" - export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" + - bash: | + make install-python + workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) + condition: ne(variables['CacheRestored-Pytest'], 'true') + displayName: Setup pytests + # Smoketests + - template: "azure/components/aws-assume-role.yml@common" + parameters: + role: "auto-ops" + profile: "apm_ptl" + - bash: | + wait + sleep 350 + export RELEASE_RELEASEID=$(Build.BuildId) + export SOURCE_COMMIT_ID=$(Build.SourceVersion) + export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" + export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" + export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" + export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" + export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="" + export AUTHENTICATE_URL="" + export OAUTH_PROXY="oauth2" + export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" + export APIGEE_API_TOKEN="$(secret.AccessToken)" + export REDIRECT_URI="https://example.org/callback" + export CLIENT_ID="$(CLIENT_ID_INT)" + export CLIENT_SECRET="$(CLIENT_SECRET_INT)" + export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" + export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" - make prod-smoketest - workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - displayName: Run smoketests - - task: PublishTestResults@2 - displayName: "Publish smoketest results" - condition: always() - inputs: - testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml" - failTaskOnFailedTests: true + make prod-smoketest + workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) + displayName: Run smoketests + - task: PublishTestResults@2 + displayName: "Publish smoketest results" + condition: always() + inputs: + testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml" + failTaskOnFailedTests: true diff --git a/azure/templates/run-tests.yml b/azure/templates/run-tests.yml index 0d25fba1..98f3b739 100644 --- a/azure/templates/run-tests.yml +++ b/azure/templates/run-tests.yml @@ -1,67 +1,67 @@ parameters: - - name: smoke_tests - type: boolean - displayName: Toggle smoke tests - default: false + - name: smoke_tests + type: boolean + displayName: Toggle smoke tests + default: false steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: "3.9" + - task: UsePythonVersion@0 + inputs: + versionSpec: "3.9" - - task: s3-cache-action@1 - inputs: - key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock" - location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv" - debug: true - alias: "Pytest" - displayName: cache pytest dependencies + - task: s3-cache-action@1 + inputs: + key: "poetry | $(SERVICE_NAME) | $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/poetry.lock" + location: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/.venv" + debug: true + alias: "Pytest" + displayName: cache pytest dependencies - - bash: | - make install-python - workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - condition: ne(variables['CacheRestored-Pytest'], 'true') - displayName: Setup pytests - # Smoketests. - - ${{ if parameters.smoke_tests }}: - - template: "azure/components/aws-assume-role.yml@common" - parameters: - role: "auto-ops" - profile: "apm_ptl" - - template: "azure/components/get-aws-secrets-and-ssm-params.yml@common" - parameters: - secret_file_ids: - - ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY - - ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY - - ptl/azure-devops/summary-care-record/JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY - config_ids: - - /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_ID - - /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_SECRET - - bash: | - wait - sleep 350 - export RELEASE_RELEASEID=$(Build.BuildId) - export SOURCE_COMMIT_ID=$(Build.SourceVersion) - export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" - export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" - export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" - export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" - export OAUTH_PROXY="oauth2-mock" - export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" - export APIGEE_API_TOKEN="$(secret.AccessToken)" - export REDIRECT_URI="https://example.org/callback" - export CLIENT_ID="$(CLIENT_ID)" - export CLIENT_SECRET="$(CLIENT_SECRET)" - export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" - export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY)" - export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" - export AUTHENTICATE_URL="https://nhsd-apim-testing-internal-dev.herokuapp.com/" - make smoketest - workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) - displayName: Run smoketests - - task: PublishTestResults@2 - displayName: "Publish smoketest results" - condition: always() - inputs: - testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml" - failTaskOnFailedTests: true + - bash: | + make install-python + workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) + condition: ne(variables['CacheRestored-Pytest'], 'true') + displayName: Setup pytests + # Smoketests. + - ${{ if parameters.smoke_tests }}: + - template: "azure/components/aws-assume-role.yml@common" + parameters: + role: "auto-ops" + profile: "apm_ptl" + - template: "azure/components/get-aws-secrets-and-ssm-params.yml@common" + parameters: + secret_file_ids: + - ptl/app-credentials/jwt_testing/non-prod/JWT_TESTING_PRIVATE_KEY + - ptl/app-credentials/jwt_testing/non-prod/ID_TOKEN_NHS_LOGIN_PRIVATE_KEY + - ptl/azure-devops/summary-care-record/JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY + config_ids: + - /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_ID + - /ptl/azure-devops/env-internal-dev/test-app/internal-testing-internal-dev/CLIENT_SECRET + - bash: | + wait + sleep 350 + export RELEASE_RELEASEID=$(Build.BuildId) + export SOURCE_COMMIT_ID=$(Build.SourceVersion) + export APIGEE_ENVIRONMENT="$(ENVIRONMENT)" + export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" + export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" + export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" + export OAUTH_PROXY="oauth2-mock" + export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" + export APIGEE_API_TOKEN="$(secret.AccessToken)" + export REDIRECT_URI="https://example.org/callback" + export CLIENT_ID="$(CLIENT_ID)" + export CLIENT_SECRET="$(CLIENT_SECRET)" + export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" + export JWT_PRIVATE_KEY_APP_RESTRICTED_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_APP_RESTRICTED_PRIVATE_KEY)" + export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" + export AUTHENTICATE_URL="https://nhsd-apim-testing-internal-dev.herokuapp.com/" + make smoketest + workingDirectory: $(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME) + displayName: Run smoketests + - task: PublishTestResults@2 + displayName: "Publish smoketest results" + condition: always() + inputs: + testResultsFiles: "$(Pipeline.Workspace)/s/$(SERVICE_NAME)/$(SERVICE_ARTIFACT_NAME)/smoketest-report.xml" + failTaskOnFailedTests: true From 5e521fe2fc7266c3cc0d1f6488b4491b518bf20f Mon Sep 17 00:00:00 2001 From: stuart mcneill Date: Tue, 17 Jun 2025 17:16:14 +0100 Subject: [PATCH 13/13] Approved