diff --git a/.github/workflows/subscribe-to-mns.yml b/.github/workflows/subscribe-to-mns.yml index c49f68eae..cd8e8a02a 100644 --- a/.github/workflows/subscribe-to-mns.yml +++ b/.github/workflows/subscribe-to-mns.yml @@ -12,15 +12,12 @@ on: sandbox: required: true type: string - secrets: - AWS_ASSUME_ROLE: - required: true permissions: pull-requests: write id-token: write # This is required for requesting the JWT contents: read # This is required for actions/checkout jobs: - batch_update_build_docker_image: + placeholder: runs-on: ubuntu-latest environment: ${{ inputs.environment }} defaults: @@ -28,6 +25,6 @@ jobs: working-directory: lambdas steps: - name: Placeholder - run: | - echo "Running placeholder job on ${inputs.sandbox}" + run: | + echo "Running placeholder job on ${inputs.sandbox}" diff --git a/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/download_lloyd_george_workflow.cy.js b/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/download_lloyd_george_workflow.cy.js index 972cf3c4a..802440d69 100644 --- a/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/download_lloyd_george_workflow.cy.js +++ b/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/download_lloyd_george_workflow.cy.js @@ -171,9 +171,6 @@ describe('GP Workflow: View Lloyd George record', () => { // Assert contents of page when downloading cy.getByTestId('lloyd-george-download-header').should('exist'); cy.getByTestId('cancel-download-link').should('exist'); - cy.getByTestId('download-file-header-' + testFiles.length + '-files').should( - 'exist', - ); // Assert contents of page after download cy.wait('@documentManifestCompleted'); @@ -225,7 +222,6 @@ describe('GP Workflow: View Lloyd George record', () => { cy.title().should('eq', downloadingPageTitle); // Assert contents of page when downloading cy.getByTestId('lloyd-george-download-header').should('exist'); - cy.getByTestId('download-file-header-2-files').should('exist'); cy.getByTestId('cancel-download-link').should('exist'); @@ -289,8 +285,6 @@ describe('GP Workflow: View Lloyd George record', () => { cy.title().should('eq', downloadingPageTitle); // Assert contents of page when downloading cy.getByTestId('lloyd-george-download-header').should('exist'); - cy.getByTestId('download-file-header-1-files').should('exist'); - cy.getByTestId('cancel-download-link').should('exist'); // Assert contents of page after download diff --git a/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/view_lloyd_george_is_bsol_workflow.cy.js b/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/view_lloyd_george_is_bsol_workflow.cy.js index d60e248df..f9f13127f 100644 --- a/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/view_lloyd_george_is_bsol_workflow.cy.js +++ b/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/view_lloyd_george_is_bsol_workflow.cy.js @@ -95,8 +95,7 @@ describe('GP Workflow: View Lloyd George record', () => { assertPatientInfo(); cy.getByTestId('pdf-card') .should('include.text', 'Lloyd George record') - .should('include.text', 'Last updated: 09 October 2023 at 15:41:38') - .should('include.text', '12 files | File size: 502 KB | File format: PDF'); + .should('include.text', 'Last updated: 09 October 2023 at 15:41:38'); cy.getByTestId('pdf-viewer').should('be.visible'); // Act - open full screen view diff --git a/app/cypress/e2e/0-ndr-core-tests/homepage.cy.js b/app/cypress/e2e/0-ndr-core-tests/homepage.cy.js index 3472e96df..ba635d03e 100644 --- a/app/cypress/e2e/0-ndr-core-tests/homepage.cy.js +++ b/app/cypress/e2e/0-ndr-core-tests/homepage.cy.js @@ -34,7 +34,7 @@ describe('Home Page', () => { }); it('displays service banner', { tags: 'regression' }, () => { - cy.get('.govuk-phase-banner__content__tag').should('have.text', 'New Service'); + cy.get('.govuk-phase-banner__content__tag').should('have.text', 'New service'); cy.get('.govuk-phase-banner__text').should( 'have.text', 'Your feedback will help us to improve this service.', diff --git a/app/src/components/blocks/_lloydGeorge/lloydGeorgeDownloadStage/LloydGeorgeDownloadStage.test.tsx b/app/src/components/blocks/_lloydGeorge/lloydGeorgeDownloadStage/LloydGeorgeDownloadStage.test.tsx index 98869324b..92712f6d7 100644 --- a/app/src/components/blocks/_lloydGeorge/lloydGeorgeDownloadStage/LloydGeorgeDownloadStage.test.tsx +++ b/app/src/components/blocks/_lloydGeorge/lloydGeorgeDownloadStage/LloydGeorgeDownloadStage.test.tsx @@ -220,9 +220,9 @@ describe('LloydGeorgeDownloadStage', () => { const renderComponent = (history: MemoryHistory, propsOverride?: Partial) => { const props: Omit = { - numberOfFiles: mockPdf.numberOfFiles, deleteAfterDownload: false, ...propsOverride, + numberOfFiles: mockPdf.numberOfFiles, }; return render( diff --git a/app/src/components/blocks/_lloydGeorge/lloydGeorgeDownloadStage/LloydGeorgeDownloadStage.tsx b/app/src/components/blocks/_lloydGeorge/lloydGeorgeDownloadStage/LloydGeorgeDownloadStage.tsx index ae802cba1..d43a23340 100644 --- a/app/src/components/blocks/_lloydGeorge/lloydGeorgeDownloadStage/LloydGeorgeDownloadStage.tsx +++ b/app/src/components/blocks/_lloydGeorge/lloydGeorgeDownloadStage/LloydGeorgeDownloadStage.tsx @@ -41,7 +41,7 @@ function LloydGeorgeDownloadStage({ filename: '', }); const linkRef = useRef(null); - + const pageDownloadCountId = 'download-file-header-' + numberOfFiles + '-files'; const mounted = useRef(false); const navigate = useNavigate(); const { mockLocal } = useConfig(); @@ -49,8 +49,6 @@ function LloydGeorgeDownloadStage({ const nhsNumber = patientDetails?.nhsNumber ?? ''; const [delayTimer, setDelayTimer] = useState(); - const pageDownloadCountId = 'download-file-header-' + numberOfFiles + '-files'; - const progressTimer = useMemo(() => { return new FakeProgress({ timeConstant: timeToComplete, @@ -145,7 +143,6 @@ function LloydGeorgeDownloadStage({ navigate, mockLocal, selectedDocuments, - numberOfFiles, ]); const pageHeader = 'Downloading documents'; diff --git a/app/src/components/blocks/_lloydGeorge/lloydGeorgeRecordDetails/LloydGeorgeRecordDetails.test.tsx b/app/src/components/blocks/_lloydGeorge/lloydGeorgeRecordDetails/LloydGeorgeRecordDetails.test.tsx index 8f095c460..d25188220 100644 --- a/app/src/components/blocks/_lloydGeorge/lloydGeorgeRecordDetails/LloydGeorgeRecordDetails.test.tsx +++ b/app/src/components/blocks/_lloydGeorge/lloydGeorgeRecordDetails/LloydGeorgeRecordDetails.test.tsx @@ -1,7 +1,6 @@ import { render, screen } from '@testing-library/react'; import LgRecordDetails, { Props } from './LloydGeorgeRecordDetails'; import { buildLgSearchResult } from '../../../../helpers/test/testBuilders'; -import formatFileSize from '../../../../helpers/utils/formatFileSize'; const mockPdf = buildLgSearchResult(); @@ -19,11 +18,6 @@ describe('LloydGeorgeRecordDetails', () => { renderComponent(); expect(screen.getByText(`Last updated: ${mockPdf.lastUpdated}`)).toBeInTheDocument(); - expect(screen.getByText(`${mockPdf.numberOfFiles} files`)).toBeInTheDocument(); - expect( - screen.getByText(`File size: ${formatFileSize(mockPdf.totalFileSizeInBytes)}`), - ).toBeInTheDocument(); - expect(screen.getByText('File format: PDF')).toBeInTheDocument(); }); }); }); @@ -31,8 +25,6 @@ describe('LloydGeorgeRecordDetails', () => { const renderComponent = (propsOverride?: Partial) => { const props: Props = { lastUpdated: mockPdf.lastUpdated, - numberOfFiles: mockPdf.numberOfFiles, - totalFileSizeInBytes: mockPdf.totalFileSizeInBytes, ...propsOverride, }; return render(); diff --git a/app/src/components/blocks/_lloydGeorge/lloydGeorgeRecordDetails/LloydGeorgeRecordDetails.tsx b/app/src/components/blocks/_lloydGeorge/lloydGeorgeRecordDetails/LloydGeorgeRecordDetails.tsx index 9105d05e0..3042f63d0 100644 --- a/app/src/components/blocks/_lloydGeorge/lloydGeorgeRecordDetails/LloydGeorgeRecordDetails.tsx +++ b/app/src/components/blocks/_lloydGeorge/lloydGeorgeRecordDetails/LloydGeorgeRecordDetails.tsx @@ -1,27 +1,16 @@ import React from 'react'; -import formatFileSize from '../../../../helpers/utils/formatFileSize'; export type Props = { lastUpdated: string; - numberOfFiles: number; - totalFileSizeInBytes: number; }; -function LloydGeorgeRecordDetails({ lastUpdated, numberOfFiles, totalFileSizeInBytes }: Props) { +function LloydGeorgeRecordDetails({ lastUpdated }: Props) { return (
Last updated: {lastUpdated}
-
- {numberOfFiles} files - {' | '} - File size: {formatFileSize(totalFileSizeInBytes)} - {' | '} - File format: PDF - {' |'} -
); diff --git a/app/src/components/blocks/_lloydGeorge/lloydGeorgeViewRecordStage/LloydGeorgeViewRecordStage.test.tsx b/app/src/components/blocks/_lloydGeorge/lloydGeorgeViewRecordStage/LloydGeorgeViewRecordStage.test.tsx index 7b3edf11f..607f541db 100644 --- a/app/src/components/blocks/_lloydGeorge/lloydGeorgeViewRecordStage/LloydGeorgeViewRecordStage.test.tsx +++ b/app/src/components/blocks/_lloydGeorge/lloydGeorgeViewRecordStage/LloydGeorgeViewRecordStage.test.tsx @@ -9,7 +9,6 @@ import useRole from '../../../../helpers/hooks/useRole'; import useIsBSOL from '../../../../helpers/hooks/useIsBSOL'; import useConfig from '../../../../helpers/hooks/useConfig'; import { act, render, screen, waitFor } from '@testing-library/react'; -import formatFileSize from '../../../../helpers/utils/formatFileSize'; import { DOWNLOAD_STAGE } from '../../../../types/generic/downloadStage'; import { getFormattedDate } from '../../../../helpers/utils/formatDate'; import userEvent from '@testing-library/user-event'; @@ -62,11 +61,6 @@ describe('LloydGeorgeViewRecordStage', () => { expect(screen.getByText('View in full screen')).toBeInTheDocument(); expect(screen.getByText('Lloyd George record')).toBeInTheDocument(); expect(screen.getByText(`Last updated: ${mockPdf.lastUpdated}`)).toBeInTheDocument(); - expect(screen.getByText(`${mockPdf.numberOfFiles} files`)).toBeInTheDocument(); - expect( - screen.getByText(`File size: ${formatFileSize(mockPdf.totalFileSizeInBytes)}`), - ).toBeInTheDocument(); - expect(screen.getByText('File format: PDF')).toBeInTheDocument(); expect( screen.queryByText('No documents are available for this patient.'), diff --git a/app/src/components/blocks/_lloydGeorge/lloydGeorgeViewRecordStage/LloydGeorgeViewRecordStage.tsx b/app/src/components/blocks/_lloydGeorge/lloydGeorgeViewRecordStage/LloydGeorgeViewRecordStage.tsx index eec37f8ab..9d30b156d 100644 --- a/app/src/components/blocks/_lloydGeorge/lloydGeorgeViewRecordStage/LloydGeorgeViewRecordStage.tsx +++ b/app/src/components/blocks/_lloydGeorge/lloydGeorgeViewRecordStage/LloydGeorgeViewRecordStage.tsx @@ -97,8 +97,6 @@ function LloydGeorgeViewRecordStage({ const recordDetailsProps: RecordDetailsProps = { downloadStage, lastUpdated, - numberOfFiles, - totalFileSizeInBytes, }; const pageHeader = 'Available records'; @@ -258,17 +256,9 @@ function LloydGeorgeViewRecordStage({ ); } -type RecordDetailsProps = Pick< - Props, - 'downloadStage' | 'lastUpdated' | 'numberOfFiles' | 'totalFileSizeInBytes' ->; +type RecordDetailsProps = Pick; -const RecordDetails = ({ - downloadStage, - lastUpdated, - numberOfFiles, - totalFileSizeInBytes, -}: RecordDetailsProps) => { +const RecordDetails = ({ downloadStage, lastUpdated }: RecordDetailsProps) => { switch (downloadStage) { case DOWNLOAD_STAGE.INITIAL: case DOWNLOAD_STAGE.PENDING: @@ -277,8 +267,6 @@ const RecordDetails = ({ case DOWNLOAD_STAGE.SUCCEEDED: { const detailsProps = { lastUpdated, - numberOfFiles, - totalFileSizeInBytes, }; return ; } diff --git a/app/src/components/layout/footer/Footer.tsx b/app/src/components/layout/footer/Footer.tsx index a8f555785..89728ce44 100644 --- a/app/src/components/layout/footer/Footer.tsx +++ b/app/src/components/layout/footer/Footer.tsx @@ -28,7 +28,10 @@ function Footer() { Service updates - © {'Crown copyright'} + {/* TODO - PRMP-1348: Remove hardcoded styling below if no longer required */} + + © {'NHS England'} + ); } diff --git a/app/src/components/layout/phaseBanner/PhaseBanner.test.tsx b/app/src/components/layout/phaseBanner/PhaseBanner.test.tsx index 685fb953d..5be57d777 100644 --- a/app/src/components/layout/phaseBanner/PhaseBanner.test.tsx +++ b/app/src/components/layout/phaseBanner/PhaseBanner.test.tsx @@ -20,7 +20,7 @@ describe('PhaseBanner', () => { it('renders PhaseBanner with content', () => { renderComponent(); - expect(screen.getByText('New Service')).toBeInTheDocument(); + expect(screen.getByText('New service')).toBeInTheDocument(); expect(screen.getByText(/Your/i)).toBeInTheDocument(); expect(screen.getByText(/feedback/i)).toBeInTheDocument(); diff --git a/app/src/components/layout/phaseBanner/PhaseBanner.tsx b/app/src/components/layout/phaseBanner/PhaseBanner.tsx index 61b66c048..fd24c6b8a 100644 --- a/app/src/components/layout/phaseBanner/PhaseBanner.tsx +++ b/app/src/components/layout/phaseBanner/PhaseBanner.tsx @@ -24,7 +24,7 @@ function PhaseBanner() {
- New Service + New service

Your {linkToFeedbackPage} will help us to improve this service. diff --git a/app/src/pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage.test.tsx b/app/src/pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage.test.tsx index 6fc97a136..0872260b1 100644 --- a/app/src/pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage.test.tsx +++ b/app/src/pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage.test.tsx @@ -7,7 +7,6 @@ import { } from '../../helpers/test/testBuilders'; import { getFormattedDate } from '../../helpers/utils/formatDate'; import axios from 'axios'; -import formatFileSize from '../../helpers/utils/formatFileSize'; import usePatient from '../../helpers/hooks/usePatient'; import useConfig from '../../helpers/hooks/useConfig'; import useRole from '../../helpers/hooks/useRole'; @@ -99,20 +98,6 @@ describe('LloydGeorgeRecordPage', () => { }); }); - it('calls refreshRecord and updates state when successful', async () => { - const lgResult = buildLgSearchResult(); - mockAxios.post.mockResolvedValue({ data: { jobStatus: 'Complete' } }); - mockAxios.get.mockResolvedValue({ data: lgResult }); - - renderPage(history); - - await waitFor(async () => { - expect(screen.getByText(`${lgResult.numberOfFiles} files`)).toBeInTheDocument(); - }); - - expect(screen.getByText('File format: PDF')).toBeInTheDocument(); - }); - it('renders initial lg record view with no docs available text if lambda return records status is uploading for more than 3 min', async () => { const errorResponse = { response: { @@ -199,12 +184,6 @@ describe('LloydGeorgeRecordPage', () => { expect(screen.getByText('Lloyd George record')).toBeInTheDocument(); expect(screen.queryByText('No documents are available')).not.toBeInTheDocument(); - - expect(screen.getByText(`${lgResult.numberOfFiles} files`)).toBeInTheDocument(); - expect( - screen.getByText(`File size: ${formatFileSize(lgResult.totalFileSizeInBytes)}`), - ).toBeInTheDocument(); - expect(screen.getByText('File format: PDF')).toBeInTheDocument(); }); describe('Accessibility', () => { diff --git a/app/src/styles/App.scss b/app/src/styles/App.scss index 096f72080..945b40c1d 100644 --- a/app/src/styles/App.scss +++ b/app/src/styles/App.scss @@ -793,6 +793,10 @@ $hunit: '%'; border: none; } +.govuk-phase-banner__content__tag { + text-transform: none; +} + .govuk-warning-text__icon { border: 3px solid $color_nhsuk-yellow; background: $color_nhsuk-yellow; diff --git a/lambdas/enums/snomed_codes.py b/lambdas/enums/snomed_codes.py index 9b1f3f995..299441fa5 100644 --- a/lambdas/enums/snomed_codes.py +++ b/lambdas/enums/snomed_codes.py @@ -1,9 +1,18 @@ -from enum import StrEnum +from enum import Enum +from pydantic import BaseModel -class SnomedCodesType(StrEnum): - LLOYD_GEORGE = "16521000000101" +class SnomedCode(BaseModel): + code: str + display_name: str -class SnomedCodesCategory(StrEnum): - CARE_PLAN = "734163000" + +class SnomedCodes(Enum): + LLOYD_GEORGE = SnomedCode( + code="16521000000101", display_name="Lloyd George record folder" + ) + CARE_PLAN = SnomedCode(code="734163000", display_name="Care plan") + GENERAL_MEDICAL_PRACTICE = SnomedCode( + code="1060971000000108", display_name="General practice service" + ) diff --git a/lambdas/models/nrl_fhir_document_reference.py b/lambdas/models/nrl_fhir_document_reference.py index ed1f67901..196d03ecf 100644 --- a/lambdas/models/nrl_fhir_document_reference.py +++ b/lambdas/models/nrl_fhir_document_reference.py @@ -1,5 +1,6 @@ from typing import Optional +from enums.snomed_codes import SnomedCode, SnomedCodes from fhir.resources.R4B.documentreference import DocumentReference from models.nrl_sqs_message import NrlAttachment from pydantic import BaseModel, ConfigDict @@ -10,9 +11,11 @@ class FhirDocumentReference(BaseModel): model_config = ConfigDict(alias_generator=to_camel) nhs_number: str custodian: str - snomed_code_doc_type: str = "None" - snomed_code_category: str = "None" - snomed_code_category_display: str = "Care plan" + snomed_code_doc_type: SnomedCode = SnomedCodes.LLOYD_GEORGE.value + snomed_code_category: SnomedCode = SnomedCodes.CARE_PLAN.value + snomed_code_practice_setting: SnomedCode = ( + SnomedCodes.GENERAL_MEDICAL_PRACTICE.value + ) attachment: Optional[NrlAttachment] = NrlAttachment() def build_fhir_dict(self): @@ -36,7 +39,8 @@ def build_fhir_dict(self): "coding": [ { "system": snomed_url, - "code": self.snomed_code_doc_type, + "code": self.snomed_code_doc_type.code, + "display": self.snomed_code_doc_type.display_name, } ] }, @@ -45,8 +49,8 @@ def build_fhir_dict(self): "coding": [ { "system": snomed_url, - "code": self.snomed_code_category, - "display": self.snomed_code_category_display, + "code": self.snomed_code_category.code, + "display": self.snomed_code_category.display_name, } ] } @@ -71,5 +75,16 @@ def build_fhir_dict(self): }, } ], + "context": { + "practiceSetting": { + "coding": [ + { + "system": snomed_url, + "code": self.snomed_code_practice_setting.code, + "display": self.snomed_code_practice_setting.display_name, + } + ] + } + }, } return DocumentReference(**structure_json) diff --git a/lambdas/models/nrl_sqs_message.py b/lambdas/models/nrl_sqs_message.py index c6c82c22d..aa3b9dbdd 100644 --- a/lambdas/models/nrl_sqs_message.py +++ b/lambdas/models/nrl_sqs_message.py @@ -1,6 +1,6 @@ from typing import Optional -from enums.snomed_codes import SnomedCodesCategory, SnomedCodesType +from enums.snomed_codes import SnomedCode, SnomedCodes from pydantic import AliasGenerator, BaseModel, ConfigDict from pydantic.alias_generators import to_camel @@ -17,12 +17,16 @@ class NrlAttachment(BaseModel): class NrlSqsMessage(BaseModel): model_config = ConfigDict( - alias_generator=AliasGenerator(serialization_alias=to_camel) + alias_generator=AliasGenerator(serialization_alias=to_camel), + use_enum_values=True, ) nhs_number: str - snomed_code_doc_type: str = SnomedCodesType.LLOYD_GEORGE - snomed_code_category: str = SnomedCodesCategory.CARE_PLAN + snomed_code_doc_type: SnomedCode = SnomedCodes.LLOYD_GEORGE.value + snomed_code_category: SnomedCode = SnomedCodes.CARE_PLAN.value + snomed_code_practice_setting: SnomedCode = ( + SnomedCodes.GENERAL_MEDICAL_PRACTICE.value + ) description: str = "" attachment: Optional[NrlAttachment] = None action: str diff --git a/lambdas/services/document_deletion_service.py b/lambdas/services/document_deletion_service.py index 09ba07e2d..8f634d780 100644 --- a/lambdas/services/document_deletion_service.py +++ b/lambdas/services/document_deletion_service.py @@ -6,7 +6,7 @@ from enums.lambda_error import LambdaError from enums.nrl_sqs_upload import NrlActionTypes from enums.s3_lifecycle_tags import S3LifecycleTags -from enums.snomed_codes import SnomedCodesCategory, SnomedCodesType +from enums.snomed_codes import SnomedCodes from enums.supported_document_types import SupportedDocumentTypes from models.document_reference import DocumentReference from models.nrl_sqs_message import NrlSqsMessage @@ -91,13 +91,13 @@ def send_sqs_message_to_remove_pointer(self, nhs_number: str): delete_nrl_message = NrlSqsMessage( nhs_number=nhs_number, action=NrlActionTypes.DELETE, - snomed_code_doc_type=SnomedCodesType.LLOYD_GEORGE, - snomed_code_category=SnomedCodesCategory.CARE_PLAN, + snomed_code_doc_type=SnomedCodes.LLOYD_GEORGE.value, + snomed_code_category=SnomedCodes.CARE_PLAN.value, ) sqs_group_id = f"NRL_delete_{uuid.uuid4()}" nrl_queue_url = os.environ["NRL_SQS_QUEUE_URL"] self.sqs_service.send_message_fifo( queue_url=nrl_queue_url, - message_body=delete_nrl_message.model_dump_json(), + message_body=delete_nrl_message.model_dump_json(exclude_unset=True), group_id=sqs_group_id, ) diff --git a/lambdas/services/nrl_api_service.py b/lambdas/services/nrl_api_service.py index 9e33d01bd..52f3ed5bb 100644 --- a/lambdas/services/nrl_api_service.py +++ b/lambdas/services/nrl_api_service.py @@ -2,8 +2,9 @@ import uuid import requests -from requests import HTTPError +from enums.snomed_codes import SnomedCode from requests.adapters import HTTPAdapter +from requests.exceptions import ConnectionError, HTTPError, Timeout from urllib3 import Retry from utils.audit_logging_setup import LoggingService from utils.exceptions import NrlApiException @@ -46,7 +47,7 @@ def create_new_pointer(self, body, retry_on_expired: bool = True): ) response.raise_for_status() logger.info("Successfully created new pointer") - except HTTPError as e: + except (ConnectionError, Timeout, HTTPError) as e: logger.error(e.response.content) if e.response.status_code == 401 and retry_on_expired: self.headers["Authorization"] = ( @@ -56,14 +57,16 @@ def create_new_pointer(self, body, retry_on_expired: bool = True): else: raise NrlApiException("Error while creating new NRL Pointer") - def get_pointer(self, nhs_number, record_type=None, retry_on_expired: bool = True): + def get_pointer( + self, nhs_number, record_type: SnomedCode = None, retry_on_expired: bool = True + ): try: self.set_x_request_id() params = { "subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{nhs_number}" } if record_type: - params["type"] = f"http://snomed.info/sct|{record_type}" + params["type"] = f"http://snomed.info/sct|{record_type.code}" response = self.session.get( url=self.endpoint, params=params, headers=self.headers ) @@ -79,7 +82,7 @@ def get_pointer(self, nhs_number, record_type=None, retry_on_expired: bool = Tru else: raise NrlApiException("Error while getting NRL Pointer") - def delete_pointer(self, nhs_number, record_type): + def delete_pointer(self, nhs_number, record_type: SnomedCode = None): search_results = self.get_pointer(nhs_number, record_type).get("entry", []) for entry in search_results: self.set_x_request_id() diff --git a/lambdas/tests/unit/handlers/test_manage_nrl_pointer_handler.py b/lambdas/tests/unit/handlers/test_manage_nrl_pointer_handler.py index c115a2202..b7a91c5c0 100644 --- a/lambdas/tests/unit/handlers/test_manage_nrl_pointer_handler.py +++ b/lambdas/tests/unit/handlers/test_manage_nrl_pointer_handler.py @@ -1,7 +1,7 @@ -import json - import pytest +from enums.snomed_codes import SnomedCodes from handlers.manage_nrl_pointer_handler import lambda_handler +from models.nrl_sqs_message import NrlAttachment, NrlSqsMessage from utils.exceptions import NrlApiException @@ -14,18 +14,18 @@ def mock_service(mocker): def build_test_sqs_message(action="create"): - SQS_Message = { - "nhs_number": "123456789", - "snomed_code_doc_type": "16521000000101", - "snomed_code_category": "734163000", - "action": action, - "attachment": { - "contentType": "application/pdf", - "url": "https://example.org/my-doc.pdf", - }, - } + doc_details = NrlAttachment( + url="https://example.org/my-doc.pdf", + ) + sqs_message = NrlSqsMessage( + nhs_number="123456789", + action=action, + snomed_code_doc_type=SnomedCodes.LLOYD_GEORGE.value, + snomed_code_category=SnomedCodes.CARE_PLAN.value, + attachment=doc_details, + ).model_dump_json() return { - "body": json.dumps(SQS_Message), + "body": sqs_message, "eventSource": "aws:sqs", } diff --git a/lambdas/tests/unit/services/test_document_deletion_service.py b/lambdas/tests/unit/services/test_document_deletion_service.py index e68c7e1a4..4a690c443 100644 --- a/lambdas/tests/unit/services/test_document_deletion_service.py +++ b/lambdas/tests/unit/services/test_document_deletion_service.py @@ -2,6 +2,7 @@ import pytest from enums.s3_lifecycle_tags import S3LifecycleTags +from enums.snomed_codes import SnomedCodes from enums.supported_document_types import SupportedDocumentTypes from services.document_deletion_service import DocumentDeletionService from tests.unit.conftest import ( @@ -227,12 +228,14 @@ def test_send_sqs_message_to_remove_pointer(mocker, mock_deletion_service): expected_message_body = ( '{{"nhs_number":"{}",' - '"snomed_code_doc_type":"16521000000101",' - '"snomed_code_category":"734163000",' - '"description":"",' - '"attachment":null,' + '"snomed_code_doc_type":{},' + '"snomed_code_category":{},' '"action":"delete"}}' - ).format(TEST_NHS_NUMBER) + ).format( + TEST_NHS_NUMBER, + SnomedCodes.LLOYD_GEORGE.value.model_dump_json(), + SnomedCodes.CARE_PLAN.value.model_dump_json(), + ) mock_deletion_service.send_sqs_message_to_remove_pointer(TEST_NHS_NUMBER) diff --git a/lambdas/tests/unit/services/test_nrl_api_service.py b/lambdas/tests/unit/services/test_nrl_api_service.py index 997a3d839..0c0241bd4 100644 --- a/lambdas/tests/unit/services/test_nrl_api_service.py +++ b/lambdas/tests/unit/services/test_nrl_api_service.py @@ -1,4 +1,5 @@ import pytest +from enums.snomed_codes import SnomedCodes from requests import Response from services.nrl_api_service import NrlApiService from tests.unit.conftest import FAKE_URL, TEST_NHS_NUMBER @@ -43,12 +44,12 @@ def test_get_end_user_ods_code(nrl_service): def test_get_pointer_with_record_type(mocker, nrl_service): - mock_type = 11111111 + mock_type = SnomedCodes.LLOYD_GEORGE.value mocker.patch("uuid.uuid4", return_value="test_uuid") mock_params = { "subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{TEST_NHS_NUMBER}", - "type": f"http://snomed.info/sct|{mock_type}", + "type": f"http://snomed.info/sct|{mock_type.code}", } mock_headers = { "Authorization": f"Bearer {ACCESS_TOKEN}", @@ -64,11 +65,11 @@ def test_get_pointer_with_record_type(mocker, nrl_service): def test_get_pointer_with_record_type_no_retry(mocker, nrl_service): - mock_type = 11111111 + mock_type = SnomedCodes.LLOYD_GEORGE.value mocker.patch("uuid.uuid4", return_value="test_uuid") mock_params = { "subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{TEST_NHS_NUMBER}", - "type": f"http://snomed.info/sct|{mock_type}", + "type": f"http://snomed.info/sct|{mock_type.code}", } mock_headers = { "Authorization": f"Bearer {ACCESS_TOKEN}", @@ -90,11 +91,11 @@ def test_get_pointer_with_record_type_no_retry(mocker, nrl_service): def test_get_pointer_with_record_type_with_retry(mocker, nrl_service): - mock_type = 11111111 + mock_type = SnomedCodes.LLOYD_GEORGE.value mocker.patch("uuid.uuid4", return_value="test_uuid") mock_params = { "subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{TEST_NHS_NUMBER}", - "type": f"http://snomed.info/sct|{mock_type}", + "type": f"http://snomed.info/sct|{mock_type.code}", } mock_headers = { "Authorization": f"Bearer {ACCESS_TOKEN}", @@ -120,7 +121,7 @@ def test_get_pointer_raise_error(nrl_service): response.status_code = 400 response._content = b"{}" - mock_type = 11111111 + mock_type = SnomedCodes.LLOYD_GEORGE.value nrl_service.session.get.return_value = response pytest.raises(NrlApiException, nrl_service.get_pointer, TEST_NHS_NUMBER, mock_type) @@ -129,7 +130,7 @@ def test_get_pointer_raise_error(nrl_service): def test_delete_pointer_with_record_type_no_record(mocker, nrl_service): - mock_type = 11111111 + mock_type = SnomedCodes.LLOYD_GEORGE.value mocker.patch("uuid.uuid4", return_value="test_uuid") nrl_response = { @@ -145,7 +146,7 @@ def test_delete_pointer_with_record_type_no_record(mocker, nrl_service): def test_delete_pointer_with_record_type_one_record(mocker, nrl_service): - mock_type = 11111111 + mock_type = SnomedCodes.LLOYD_GEORGE.value mocker.patch("uuid.uuid4", return_value="test_uuid") mock_pointer_id = "ODSCODE-1111bfb1-1111-2222-3333-4444555c666f" mock_headers = { @@ -176,7 +177,7 @@ def test_delete_pointer_with_record_type_one_record(mocker, nrl_service): def test_delete_pointer_with_record_type_more_than_one_record(mocker, nrl_service): - mock_type = 11111111 + mock_type = SnomedCodes.LLOYD_GEORGE.value mocker.patch("uuid.uuid4", return_value="test_uuid") mock_pointer_id = "ODSCODE-1111bfb1-1111-2222-3333-4444555c666" @@ -209,7 +210,7 @@ def test_delete_pointer_not_raise_error(mocker, nrl_service): response = Response() response.status_code = 400 response._content = b"{}" - mock_type = 11111111 + mock_type = SnomedCodes.LLOYD_GEORGE.value nrl_response = { "resourceType": "Bundle", "type": "searchset",