Skip to content

Commit

Permalink
[PRMP-1287] merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
NogaNHS committed Jan 17, 2025
2 parents e6d6953 + 1caa1ad commit 446f05d
Show file tree
Hide file tree
Showing 16 changed files with 136 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,29 @@ const downloadPageTitle =
'Download the Lloyd George record for this patient - Access and store digital patient documents';
const downloadingPageTitle = 'Downloading documents - Access and store digital patient documents';
const downloadCompletePageTitle = 'Download complete - Access and store digital patient documents';
const verifyPatientPageTitle =
'Patient details - Access and store digital patient documents';
const verifyPatientPageTitle = 'Patient details - Access and store digital patient documents';
const lloydGeorgeRecordPageTitle = 'Available records - Access and store digital patient documents';
const testFiles = [
{
fileName: '1of2_testy_test.pdf',
created: '2024-05-07T14:52:00.827602Z',
virusScannerResult: 'Clean',
id: 'test-id',
fileSize: 200,
},
{
fileName: '2of2_testy_test.pdf',
created: '2024-05-07T14:52:00.827602Z',
virusScannerResult: 'Clean',
id: 'test-id-2',
fileSize: 200,
},
{
fileName: '1of1_lone_test_file.pdf',
created: '2024-01-01T14:52:00.827602Z',
virusScannerResult: 'Clean',
id: 'test-id-3',
fileSize: 200,
},
];

Expand All @@ -40,6 +42,7 @@ const singleTestFile = [
created: '2024-01-01T14:52:00.827602Z',
virusScannerResult: 'Clean',
id: 'test-id-3',
fileSize: 200,
},
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ describe('LloydGeorgeDownloadStage', () => {

const expectedTestId = 'download-file-header-' + mockPdf.numberOfFiles + '-files';
expect(screen.getByTestId(expectedTestId)).toBeInTheDocument();
expect(screen.getByTestId('cancel-download-link')).toHaveTextContent(
'Cancel and return to patient record',
);
});

it('renders a progress bar', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ function LloydGeorgeDownloadStage({
navigate(routes.LLOYD_GEORGE);
}}
>
Cancel
Cancel and return to patient record
</Link>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import usePatient from '../../../../helpers/hooks/usePatient';
import { LinkProps } from 'react-router-dom';
import LloydGeorgeSelectSearchResults, { Props } from './LloydGeorgeSelectSearchResults';
import userEvent from '@testing-library/user-event';
import { routes } from '../../../../types/generic/routes';
import { SEARCH_AND_DOWNLOAD_STATE } from '../../../../types/pages/documentSearchResultsPage/types';
import { runAxeTest } from '../../../../helpers/test/axeTestHelper';

Expand Down Expand Up @@ -57,6 +56,20 @@ describe('LloydGeorgeSelectSearchResults', () => {
expect(screen.getByTestId('toggle-selection-btn')).toBeInTheDocument();
});

it('renders the correct table headers', () => {
renderComponent({ selectedDocuments: mockSelectedDocuments });

const headers = screen.getAllByRole('columnheader');
const expectedHeaders = ['Selected', 'Filename', 'Upload date', 'File size'];

expectedHeaders.forEach((headerText, index) => {
expect(headers[index]).toHaveTextContent(headerText);
});

const filesTable = screen.getByTestId('available-files-table-title');
expect(filesTable).toHaveTextContent(/bytes|KB|MB|GB/);
});

it('shows error box when download selected files button is clicked but no files selected', async () => {
renderComponent({ selectedDocuments: [] });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { SEARCH_AND_DOWNLOAD_STATE } from '../../../../types/pages/documentSearc
import ErrorBox from '../../../layout/errorBox/ErrorBox';
import PatientSummary from '../../../generic/patientSummary/PatientSummary';
import BackButton from '../../../generic/backButton/BackButton';
import formatFileSize from '../../../../helpers/utils/formatFileSize';

export type Props = {
searchResults: Array<SearchResult>;
Expand Down Expand Up @@ -108,6 +109,7 @@ const AvailableFilesTable = ({
)}
<Table.Cell className={'table-column-header'}>Filename</Table.Cell>
<Table.Cell className={'table-column-header'}>Upload date</Table.Cell>
<Table.Cell className={'table-column-header'}>File size</Table.Cell>
</Table.Row>
</Table.Head>
<Table.Body>
Expand Down Expand Up @@ -147,6 +149,12 @@ const AvailableFilesTable = ({
>
{getFormattedDatetime(new Date(result.created))}
</Table.Cell>
<Table.Cell
id={'available-files-row-' + index + '-file-size'}
data-testid="file-size"
>
{formatFileSize(result.fileSize)}
</Table.Cell>
</Table.Row>
))}
</Table.Body>
Expand Down
1 change: 1 addition & 0 deletions app/src/helpers/test/testBuilders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ const buildSearchResult = (searchResultOverride?: Partial<SearchResult>) => {
created: moment().format(),
virusScannerResult: 'Clean',
ID: '1234qwer-241ewewr',
fileSize: 224,
...searchResultOverride,
};
return result;
Expand Down
35 changes: 35 additions & 0 deletions app/src/helpers/utils/formatFileSize.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import formatFileSize from './formatFileSize';

describe('formatFileSize', () => {
it('returns rounded file size formats for valid inputs', () => {
expect(formatFileSize(0)).toBe('0 bytes');
expect(formatFileSize(-0)).toBe('0 bytes');
expect(formatFileSize(1)).toBe('1 bytes');
expect(formatFileSize(1.5)).toBe('2 bytes');

expect(formatFileSize(1023)).toBe('1023 bytes');
expect(formatFileSize(1024)).toBe('1 KB');
expect(formatFileSize(1025)).toBe('1 KB');

expect(formatFileSize(1535)).toBe('1 KB');
expect(formatFileSize(1536)).toBe('2 KB');
expect(formatFileSize(2048)).toBe('2 KB');

expect(formatFileSize(Math.pow(2, 20) - 1)).toBe('1024 KB');
expect(formatFileSize(Math.pow(2, 20))).toBe('1 MB');
expect(formatFileSize(Math.pow(2, 20) + 1)).toBe('1 MB');

expect(formatFileSize(Math.pow(2, 30) - 1)).toBe('1024 MB');
expect(formatFileSize(Math.pow(2, 30))).toBe('1 GB');
expect(formatFileSize(Math.pow(2, 30) + 1)).toBe('1 GB');
});

it('throws "Invalid file size" exception for invalid inputs', () => {
expect(() => formatFileSize(Number.MIN_SAFE_INTEGER)).toThrow('Invalid file size');
expect(() => formatFileSize(-1)).toThrow('Invalid file size');
expect(() => formatFileSize(NaN)).toThrow('Invalid file size');
expect(() => formatFileSize(undefined as unknown as number)).toThrow('Invalid file size');
expect(() => formatFileSize(Math.pow(2, 40))).toThrow('Invalid file size'); // 1TB
expect(() => formatFileSize(Number.MAX_SAFE_INTEGER)).toThrow('Invalid file size');
});
});
4 changes: 2 additions & 2 deletions app/src/pages/privacyPage/PrivacyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ function PrivacyPage() {
</section>

<section>
<h1>Our permission to process and store patient data</h1>
<h2>Our permission to process and store patient data</h2>
<p>
This service has legal permission to process and store patient data through the
<strong> National Data Processing Deed</strong>.
Expand Down Expand Up @@ -137,7 +137,7 @@ function PrivacyPage() {
</section>

<section>
<h1>Contact us</h1>
<h2>Contact us</h2>
<p>
If you have any questions about the National Data Processing Deed, or our
privacy policy, you can contact the team on{' '}
Expand Down
1 change: 1 addition & 0 deletions app/src/types/generic/searchResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export type SearchResult = {
created: string;
virusScannerResult: string;
ID: string;
fileSize: number;
};
12 changes: 12 additions & 0 deletions lambdas/handlers/manage_nrl_pointer_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from datetime import datetime

from enums.nrl_sqs_upload import NrlActionTypes
from models.fhir.R4.nrl_fhir_document_reference import DocumentReferenceInfo
Expand Down Expand Up @@ -56,6 +57,17 @@ def lambda_handler(event, context):
nrl_api_service.create_new_pointer(
document.model_dump(exclude_none=True)
)

logger.info(
f"Create pointer request: Body: {json.loads(document)}, "
f"RequestURL: {nrl_api_service.endpoint}, "
"HTTP Verb: POST, "
f"NHS Number: {nrl_message.nhs_number}, "
f"ODS Code: {nrl_api_service.end_user_ods_code}, "
f"Datetime: {int(datetime.now().timestamp())} "
)
nrl_api_service.create_new_pointer(json.loads(document))

case NrlActionTypes.DELETE:
nrl_api_service.delete_pointer(
nrl_message.nhs_number, nrl_message.snomed_code_doc_type
Expand Down
Empty file.
1 change: 1 addition & 0 deletions lambdas/services/bulk_upload_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ def handle_sqs_message(self, message: dict):
)
doc_details = Attachment(
url=document_api_endpoint,
content_type="application/pdf",
)
nrl_sqs_message = NrlSqsMessage(
nhs_number=staging_metadata.nhs_number,
Expand Down
19 changes: 15 additions & 4 deletions lambdas/services/document_reference_search_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,21 @@ def get_document_references(self, nhs_number: str):
423, LambdaError.UploadInProgressError
)
results.extend(
document.model_dump(
include={"file_name", "created", "virus_scanner_result", "id"},
by_alias=True,
)
{
**document.model_dump(
include={
"file_name",
"created",
"virus_scanner_result",
"id",
},
by_alias=True,
),
"fileSize": self.s3_service.get_file_size(
s3_bucket_name=document.get_file_bucket(),
object_key=document.get_file_key(),
),
}
for document in documents
)
return results
Expand Down
33 changes: 32 additions & 1 deletion lambdas/services/nrl_api_service.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import uuid
from datetime import datetime

import requests
from enums.snomed_codes import SnomedCode
Expand All @@ -10,6 +11,7 @@
from utils.exceptions import NrlApiException

logger = LoggingService(__name__)
NRL_USER_ID = "National-Document-Repository"


class NrlApiService:
Expand Down Expand Up @@ -45,6 +47,11 @@ def create_new_pointer(self, body: dict, retry_on_expired: bool = True):
response = self.session.post(
url=self.endpoint, headers=self.headers, json=body
)
logger.info(
f"Create pointer response: Status code: {response.status_code}, "
f"Body: {response.json()}, "
f"Headers: {response.headers}"
)
response.raise_for_status()
logger.info("Successfully created new pointer")
except (ConnectionError, Timeout, HTTPError) as e:
Expand All @@ -70,6 +77,18 @@ def get_pointer(
response = self.session.get(
url=self.endpoint, params=params, headers=self.headers
)
logger.info(
f"Get pointer request: URL: {response.url}, "
"HTTP Verb: GET, "
f"ODS Code: {self.end_user_ods_code}, "
f"Datetime: {int(datetime.now().timestamp())}, "
f"UserID: {self.end_user_ods_code} - {NRL_USER_ID}"
)
logger.info(
f"Get pointer response: Status code: {response.status_code}, "
f"Body: {response.json()}, "
f"Headers: {response.headers}"
)
response.raise_for_status()
return response.json()
except HTTPError as e:
Expand All @@ -90,7 +109,19 @@ def delete_pointer(self, nhs_number, record_type: SnomedCode = None):
url_endpoint = self.endpoint + f"/{pointer_id}"
try:
response = self.session.delete(url=url_endpoint, headers=self.headers)
logger.info(response.json())
logger.info(
f"Delete pointer request: URL: {url_endpoint}, "
f"HTTP Verb: DELETE, "
f"ODS Code: {self.end_user_ods_code}, "
f"NHS Number: {nhs_number}, "
f"Datetime: {int(datetime.now().timestamp())}, "
f"UserID: {self.end_user_ods_code} - {NRL_USER_ID}."
)
logger.info(
f"Delete pointer response: Body: {response.json()}, "
f"Status Code: {response.status_code}, "
f"Headers: {response.headers}"
)
response.raise_for_status()
except HTTPError as e:
logger.error(e.response.json())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,22 @@
DocumentReference.model_validate(MOCK_SEARCH_RESPONSE["Items"][0])
]

MOCK_FILE_SIZE = 24000

EXPECTED_RESPONSE = {
"created": "2024-01-01T12:00:00.000Z",
"fileName": "document.csv",
"virusScannerResult": "Clean",
"ID": "3d8683b9-1665-40d2-8499-6e8302d507ff",
"fileSize": MOCK_FILE_SIZE,
}


@pytest.fixture
def patched_service(mocker, set_env):
service = DocumentReferenceSearchService()
mocker.patch.object(service, "s3_service")
mock_s3_service = mocker.patch.object(service, "s3_service")
mocker.patch.object(mock_s3_service, "get_file_size", return_value=MOCK_FILE_SIZE)
mocker.patch.object(service, "dynamo_service")
mocker.patch.object(service, "fetch_documents_from_table_with_nhs_number")
mocker.patch.object(service, "is_upload_in_process", return_value=False)
Expand Down
1 change: 1 addition & 0 deletions lambdas/tests/unit/services/test_nrl_api_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def test_create_new_pointer(nrl_service):
def test_create_new_pointer_raise_error(nrl_service):
mock_body = {"test": "tests"}
response = Response()
response._content = b"{}"
response.status_code = 400
nrl_service.session.post.return_value = response
pytest.raises(NrlApiException, nrl_service.create_new_pointer, mock_body)
Expand Down

0 comments on commit 446f05d

Please sign in to comment.