diff --git a/Cps.Fct.Hk.Ui.Interfaces/Cps.Fct.Hk.Ui.Interfaces.csproj b/Cps.Fct.Hk.Ui.Interfaces/Cps.Fct.Hk.Ui.Interfaces.csproj index 18be8d30e..8e80cd8b5 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/Cps.Fct.Hk.Ui.Interfaces.csproj +++ b/Cps.Fct.Hk.Ui.Interfaces/Cps.Fct.Hk.Ui.Interfaces.csproj @@ -24,7 +24,7 @@ - + diff --git a/Cps.Fct.Hk.Ui.Interfaces/IBulkSetUnusedService.cs b/Cps.Fct.Hk.Ui.Interfaces/IBulkSetUnusedService.cs index e73634ca9..8cec49e51 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/IBulkSetUnusedService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/IBulkSetUnusedService.cs @@ -20,6 +20,7 @@ public interface IBulkSetUnusedService /// The case ID. /// The authentication values for CMS access. /// The materials to reclassify as unused. + /// The cancellation token. /// A task that returns the bulk set response. - Task BulkSetUnusedAsync(int caseId, CmsAuthValues cmsAuthValues, IReadOnlyCollection bulkSetUnusedRequests); + Task BulkSetUnusedAsync(int caseId, CmsAuthValues cmsAuthValues, IReadOnlyCollection bulkSetUnusedRequests, CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Interfaces/ICaseActionPlanService.cs b/Cps.Fct.Hk.Ui.Interfaces/ICaseActionPlanService.cs index b716730ce..24477d087 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/ICaseActionPlanService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/ICaseActionPlanService.cs @@ -21,6 +21,7 @@ public interface ICaseActionPlanService /// The unique identifier of the case for which an action plan is being created. /// The action plan to create. /// The authentication values used to authorize the request to retrieve communications. + /// The cancellation token. /// A task that represents the asynchronous operation. The task result contains a read-only object. - Task AddCaseActionPlanAsync(string urn, int caseId, AddCaseActionPlanRequest addCaseActionPlanRequest, CmsAuthValues cmsAuthValues); + Task AddCaseActionPlanAsync(string urn, int caseId, AddCaseActionPlanRequest addCaseActionPlanRequest, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Interfaces/ICaseDefendantsService.cs b/Cps.Fct.Hk.Ui.Interfaces/ICaseDefendantsService.cs index f2815c016..e759a9b23 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/ICaseDefendantsService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/ICaseDefendantsService.cs @@ -18,6 +18,7 @@ public interface ICaseDefendantsService /// /// Unique identifier of the case to retrieve defendants for. /// The authentication values used to authorize the request to retrieve defendants. + /// Cancelltion token. /// A task representing the asynchronous operation. - Task GetCaseDefendantsAsync(int caseId, CmsAuthValues cmsAuthValues); + Task GetCaseDefendantsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Interfaces/ICaseInfoService.cs b/Cps.Fct.Hk.Ui.Interfaces/ICaseInfoService.cs index 11ff8e172..9ef50d454 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/ICaseInfoService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/ICaseInfoService.cs @@ -4,6 +4,7 @@ namespace Cps.Fct.Hk.Ui.Interfaces; +using System.Threading; using System.Threading.Tasks; using Common.Dto.Request; using Common.Dto.Response.HouseKeeping; @@ -18,6 +19,7 @@ public interface ICaseInfoService /// /// The unique identifier of the case for which communications are being retrieved. /// The authentication values used to authorize the request to retrieve communications. + /// A token to cancel the asynchronous operation. Defaults to . /// A task that represents the asynchronous operation. The task result contains a read-only object. - Task GetCaseInfoAsync(int caseId, CmsAuthValues cmsAuthValues); + Task GetCaseInfoAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Interfaces/ICaseLockService.cs b/Cps.Fct.Hk.Ui.Interfaces/ICaseLockService.cs index dff2174f3..f8b2b5ee1 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/ICaseLockService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/ICaseLockService.cs @@ -18,6 +18,7 @@ public interface ICaseLockService /// /// The ID of the case to be unlocked. /// The CMS authentication values required for the API call. + /// Cancellation token. /// A task that represents the asynchronous operation. The task result contains a read-only string object. - Task CheckCaseLockAsync(int caseId, CmsAuthValues cmsAuthValues); + Task CheckCaseLockAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Interfaces/ICaseMaterialService.cs b/Cps.Fct.Hk.Ui.Interfaces/ICaseMaterialService.cs index 5d2103fd0..9cfd3135f 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/ICaseMaterialService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/ICaseMaterialService.cs @@ -78,7 +78,6 @@ public interface ICaseMaterialService /// /// The containing collections of unused materials to be mapped. /// - /// Inbox communucations. /// /// A list of objects representing the mapped unused materials. /// Returns an empty list if no materials are available. @@ -90,6 +89,7 @@ public interface ICaseMaterialService /// /// The ID of the case. /// Authentication details for accessing the CMS. + /// A token to cancel the asynchronous operation. Defaults to . /// /// A tuple containing case communications, unused materials, used statements, used exhibits, used MG forms, used other materials and exhibit producers. /// @@ -101,7 +101,7 @@ public interface ICaseMaterialService UsedMgFormsResponse, UsedOtherMaterialsResponse, ExhibitProducersResponse)> - RetrieveCaseMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues); + RetrieveCaseMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Adds materials to the list of case materials, handling duplicates by removing the existing material and adding the new one. @@ -120,6 +120,7 @@ void AddCaseMaterials(List allCaseMaterials, IEnumerable mat /// /// The unique ID of the case. /// Authorization values for CMS access. + /// A token to cancel the asynchronous operation. Defaults to . /// A representing the result of the asynchronous operation. - public Task GetExhibitProducersAsync(int caseId, CmsAuthValues cmsAuthValues); + public Task GetExhibitProducersAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Interfaces/ICommunicationService.cs b/Cps.Fct.Hk.Ui.Interfaces/ICommunicationService.cs index a25d06ef5..64b8ceece 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/ICommunicationService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/ICommunicationService.cs @@ -24,48 +24,54 @@ public interface ICommunicationService /// /// The unique identifier of the case for which communications are being retrieved. /// The authentication values used to authorize the request to retrieve communications. + /// cancellation token used to cancel the operation. /// A task that represents the asynchronous operation. The task result contains a read-only collection of objects. - Task> GetCommunicationsAsync(int caseId, CmsAuthValues cmsAuthValues); + Task> GetCommunicationsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Retrieves a collection of used statements for a specific case. /// /// The unique identifier of the case for which used statements are being retrieved. /// The authentication values used to authorize the request to retrieve the used statements. + /// cancellation token used to cancel the operation. /// A task that represents the asynchronous operation. The task result contains a object, which includes details of the used statements. - Task GetUsedStatementsAsync(int caseId, CmsAuthValues cmsAuthValues); + Task GetUsedStatementsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Retrieves a collection of used exhibits for a specific case. /// /// The unique identifier of the case for which used exhibits are being retrieved. /// The authentication values used to authorize the request to retrieve the used exhibits. + /// cancellation token used to cancel the operation. /// A task that represents the asynchronous operation. The task result contains a object, which includes details of the used exhibits. - Task GetUsedExhibitsAsync(int caseId, CmsAuthValues cmsAuthValues); + Task GetUsedExhibitsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Retrieves a collection of used MG forms for a specific case. /// /// The unique identifier of the case for which used MG forms are being retrieved. /// The authentication values used to authorize the request to retrieve the used MG forms. + /// cancellation token used to cancel the operation. /// A task that represents the asynchronous operation. The task result contains a object, which includes details of the used MG forms. - Task GetUsedMgFormsAsync(int caseId, CmsAuthValues cmsAuthValues); + Task GetUsedMgFormsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Retrieves a collection of used other materials for a specific case. /// /// The unique identifier of the case for which used other materials are being retrieved. /// The authentication values used to authorize the request to retrieve the used other materials. + /// cancellation token used to cancel the operation. /// A task that represents the asynchronous operation. The task result contains a object, which includes details of the used other materials. - Task GetUsedOtherMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues); + Task GetUsedOtherMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Retrieves a collection of unused materials for a specific case. /// /// The unique identifier of the case for which unused materials are being retrieved. /// The authentication values used to authorize the request to retrieve the unused materials. + /// cancellation token used to cancel the operation. /// A task that represents the asynchronous operation. The task result contains a object, which includes details of the unused materials. - Task GetUnusedMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues); + Task GetUnusedMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Retrieves a collection of attachments for a specific communication. @@ -73,16 +79,18 @@ public interface ICommunicationService /// The unique identifier of the communication for which attachments are being retrieved. /// The subject of the communication for which attachments are being retrieved. /// The authentication values used to authorize the request to retrieve the unused materials. + /// cancellation token used to cancel the operation. /// A task that represents the asynchronous operation. The task result contains a object, which includes attachments of the communication. - Task GetAttachmentsAsync(int communicationId, string communicationSubject, CmsAuthValues cmsAuthValues); + Task GetAttachmentsAsync(int communicationId, string communicationSubject, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Retrieves all attachments for a collection of communications. /// /// The collection of communications to obtain attachments from. /// The authentication values used to authorize the request to retrieve the unused materials. + /// cancellation token used to cancel the operation. /// A task that represents the asynchronous operation. The task result contains a list of objects. - Task> RetrieveAllAttachmentsAsync(IReadOnlyCollection communications, CmsAuthValues cmsAuthValues); + Task> RetrieveAllAttachmentsAsync(IReadOnlyCollection communications, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Maps a list of attachments to a list of objects. @@ -97,16 +105,18 @@ public interface ICommunicationService /// The ID of the case from which communications are retrieved. /// The ID of the material to find the corresponding case material document link. /// Authorization values for CMS access. + /// cancellation token used to cancel the operation. /// An object that contains the communication link if found, or a result indicating that no communication was found. - Task GetCaseMaterialLinkAsync(int caseId, int materialId, CmsAuthValues cmsAuthValues); + Task GetCaseMaterialLinkAsync(int caseId, int materialId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Retrieves list of exhibit producers required as part of exhibit reclassification for a given case Id. /// /// The unique ID of the case. /// Authorization values for CMS access. + /// cancellation token used to cancel the operation. /// A representing the result of the asynchronous operation. - public Task GetExhibitProducersAsync(int caseId, CmsAuthValues cmsAuthValues); + public Task GetExhibitProducersAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Asynchronously rename a material name with given material ID. @@ -116,16 +126,18 @@ public interface ICommunicationService /// New material name to be changed with. /// Authorization values for CMS access. /// correspondenceId. + /// cancellation token used to cancel the operation. /// An object that contains the renamed material id. - Task RenameMaterialAsync(int caseId, int materialId, string subject, CmsAuthValues cmsAuthValues, Guid correspondenceId = default); + Task RenameMaterialAsync(int caseId, int materialId, string subject, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default); /// /// Get PCD requests core info by case Id. /// /// Get PCD requests by case id. /// Authorization values for CMS access. + /// cancellation token used to cancel the operation. /// Return collection of PCD requests. - Task> GetPcdRequestCore(int caseId, CmsAuthValues cmsAuthValues); + Task> GetPcdRequestCore(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get PCD Request overview by case Id Pcd id. @@ -133,8 +145,9 @@ public interface ICommunicationService /// Get PCD requests by case id. /// Parameter to get PCD requests by PCD id. /// Authorization values for CMS access. + /// cancellation token used to cancel the operation. /// Return single PCD request information by PCD id. - Task GetPcdRequestByPcdIdAsync(int caseId, int pcdId, CmsAuthValues cmsAuthValues); + Task GetPcdRequestByPcdIdAsync(int caseId, int pcdId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get PCD review detail by case Id Pcd id. @@ -144,16 +157,16 @@ public interface ICommunicationService /// Authorization values for CMS access. /// cancellationToken values for downstream systems. /// Return single PCD request information by PCD id. - Task GetPcdReviewDetailAsync(int caseId, int pcdId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken); + Task GetPcdReviewDetailAsync(int caseId, int pcdId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get PCD review items by case Id Pcd id. /// /// Get PCD requests by case id. - /// Authorization values for CMS access. - /// cancellationToken values for downstream systems. + /// Authorization values for CMS access. + /// cancellation token used to cancel the operation. /// Return single PCD request information by PCD id. - Task> GetPcdReviewCoreAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken); + Task> GetPcdReviewCoreAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Asynchronously discard a material name with given material ID. @@ -164,8 +177,9 @@ public interface ICommunicationService /// Discard reason description for the material. /// Authorization values for CMS access. /// correspondenceId. + /// cancellation token used to cancel the operation. /// An object that contains the discarded material id. - Task DiscardMaterialAsync(int caseId, int materialId, string discardReason, string discardReasonDescription, CmsAuthValues cmsAuthValues, Guid correspondenceId = default); + Task DiscardMaterialAsync(int caseId, int materialId, string discardReason, string discardReasonDescription, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default); /// /// Asynchronously sets material read or unread status.. @@ -174,8 +188,9 @@ public interface ICommunicationService /// Material read or unread status. /// Authorization values for CMS access. /// correspondenceId. + /// cancellationToken values for downstream systems. /// An object that contains the renamed material id. - Task SetMaterialReadStatusAsync(int materialId, MaterialReadStatusType action, CmsAuthValues cmsAuthValues, Guid correspondenceId = default); + Task SetMaterialReadStatusAsync(int materialId, MaterialReadStatusType action, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default); /// /// Updates exhibit given an exhibit request. @@ -184,8 +199,9 @@ public interface ICommunicationService /// The update exhibit request model. /// Authorization values for CMS access. /// correspondenceId. + /// cancellationToken values for downstream systems. /// A representing the result of the asynchronous operation. - public Task UpdateExhibitAsync(int caseId, UpdateExhibitRequest exhibit, CmsAuthValues cmsAuthValues, Guid correspondenceId = default); + public Task UpdateExhibitAsync(int caseId, UpdateExhibitRequest exhibit, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default); /// /// Update statement given a statement request. @@ -194,16 +210,18 @@ public interface ICommunicationService /// The update statement request. /// Authorization values for CMS access. /// correspondenceId. + /// cancellation token used to cancel the operation. /// A representing the result of the asynchronous operation. - public Task UpdateStatementAsync(int caseId, UpdateStatementRequest statement, CmsAuthValues cmsAuthValues, Guid correspondenceId = default); + public Task UpdateStatementAsync(int caseId, UpdateStatementRequest statement, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default); /// /// Get First Initial Review Get Case History. /// /// The caseId. /// The CMS authentication values required for the API call. + /// cancellation token used to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task FirstInitialReviewGetCaseHistoryAsync(int caseId, CmsAuthValues cmsAuthValues); + Task FirstInitialReviewGetCaseHistoryAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// @@ -212,17 +230,18 @@ public interface ICommunicationService /// Case id. /// History Id. /// The CMS authentication values required for the API call. - /// The cancellationToken for downstream systems. + /// The cancellation token for the operation.ram> /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetInitialReviewByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken); + Task GetInitialReviewByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// GetHistoryEvents. /// /// The caseId. /// The CMS authentication values required for the API call. + /// The cancellation token for the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task> GetHistoryEventsAsync(int caseId, CmsAuthValues cmsAuthValues); + Task> GetHistoryEventsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get Offence Charge. @@ -230,16 +249,18 @@ public interface ICommunicationService /// The caseId. /// History Id. /// The CMS authentication values required for the API call. + /// The cancellation token for the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetOffenceChargeByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues); + Task GetOffenceChargeByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get Pre Charge Decision Case History Event Details. /// /// Case Id. /// The CMS authentication values required for the API call. + /// The cancellation token for the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetPreChargeDecisionCaseHistoryEventDetailsAsync(int caseId, CmsAuthValues cmsAuthValues); + Task GetPreChargeDecisionCaseHistoryEventDetailsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get Pre Charge Decision By History Id. @@ -247,14 +268,17 @@ public interface ICommunicationService /// Case Id. /// History Id. /// The CMS authentication values required for the API call. + /// The cancellation token for the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetPreChargeDecisionByHistoryId(int caseId, int historyId, CmsAuthValues cmsAuthValues); + Task GetPreChargeDecisionByHistoryId(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get Pcd review. /// /// The case id. - /// The CMS authentication values required for the API call. + /// The CMS authentication values required for the API call.The cancellation token for the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetPcdReview(int caseId, CmsAuthValues cmsAuthValues); + Task GetPcdReview(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); } + diff --git a/Cps.Fct.Hk.Ui.Interfaces/IConversionService.cs b/Cps.Fct.Hk.Ui.Interfaces/IConversionService.cs index 93900f38c..c8bd38d7f 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/IConversionService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/IConversionService.cs @@ -17,10 +17,11 @@ public interface IConversionService /// or in a cloud blob container, depending on the implementation. /// /// The file stream result representing the downloaded document. + /// The cancellation token. /// /// A representing the asynchronous operation. The task result is true if the document is successfully saved; otherwise, false. /// - Task SaveDownloadedDocumentToTemporaryStorageAsync(FileStreamResult downloadedDocument); + Task SaveDownloadedDocumentToTemporaryStorageAsync(FileStreamResult downloadedDocument, CancellationToken cancellationToken = default); /// /// Converts the given document (e.g., a raster image or text file) to a PDF and saves the result @@ -28,22 +29,24 @@ public interface IConversionService /// /// The temporary file name of the document to be converted to PDF. /// A flag to indicate if only first page needs to be converted or all pages when set to false. + /// The cancellation token used to cancel the service. /// /// A representing the asynchronous operation. The task result is a string containing /// the URL of the saved PDF document, or null if the conversion or save operation fails. /// - Task ConvertToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true); + Task ConvertToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true, CancellationToken cancellationToken = default); /// /// Converts a raster image (e.g., a .jpg, .png file) to a PDF document and saves the result /// either to local storage or a cloud blob container. /// /// The temporary file name of the raster image to be converted. + /// The cancellation token used to cancel the service. /// /// A representing the asynchronous operation. The task result is a string containing /// the URL of the saved PDF document, or null if the conversion or save operation fails. /// - Task ConvertRasterImageToPdfDocumentAsync(string tmpFileDownloadName); + Task ConvertRasterImageToPdfDocumentAsync(string tmpFileDownloadName, CancellationToken cancellationToken = default); /// /// Extracts specific pages from a PDF document and saves them as a new PDF file. @@ -51,33 +54,36 @@ public interface IConversionService /// /// The temporary file name of the PDF document to be processed. /// A flag to indicate if only first page needs to be converted or all pages when set to false. + /// The cancellation token used to cancel the service. /// /// A representing the asynchronous operation. The task result is a string containing /// the URL of the saved PDF document, or null if the conversion or save operation fails. /// - Task ConvertPdfToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true); + Task ConvertPdfToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true, CancellationToken cancellationToken = default); /// /// Converts a text file (e.g., .txt, .csv file) to a PDF document and saves the result /// either to local storage or a cloud blob container. /// /// The temporary file name of the text file to be converted. + /// The cancellation token used to cancel the service. /// /// A representing the asynchronous operation. The task result is a string containing /// the URL of the saved PDF document, or null if the conversion or save operation fails. /// - Task ConvertTxtToPdfDocumentAsync(string tmpFileDownloadName); + Task ConvertTxtToPdfDocumentAsync(string tmpFileDownloadName, CancellationToken cancellationToken = default); /// /// Converts a html file (e.g., .hte, .htm, .html file) to a PDF document and saves the result /// either to local storage or a cloud blob container. /// /// The temporary file name of the text file to be converted. + /// The cancellation token used to cancel the service. /// /// A representing the asynchronous operation. The task result is a string containing /// the URL of the saved PDF document, or null if the conversion or save operation fails. /// - Task ConvertHtmlToPdfDocumentAsync(string tmpFileDownloadName); + Task ConvertHtmlToPdfDocumentAsync(string tmpFileDownloadName, CancellationToken cancellationToken = default); /// /// Converts a doc file (e.g., .doc, .docx, .docm file) to a PDF document and saves the result @@ -85,22 +91,24 @@ public interface IConversionService /// /// The temporary file name of the text file to be converted. /// A flag to indicate if only first page needs to be converted or all pages when set to false. + /// The cancellation token used to cancel the service. /// /// A representing the asynchronous operation. The task result is a string containing /// the URL of the saved PDF document, or null if the conversion or save operation fails. /// - Task ConvertDocToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true); + Task ConvertDocToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true, CancellationToken cancellationToken = default); /// /// Converts an xml file (e.g., .xml file) to a PDF document and saves the result /// either to local storage or a cloud blob container. /// /// The temporary file name of the text file to be converted. + /// The cancellation token used to cancel the service. /// /// A representing the asynchronous operation. The task result is a string containing /// the URL of the saved PDF document, or null if the conversion or save operation fails. /// - Task ConvertXmlToPdfDocumentAsync(string tmpFileDownloadName); + Task ConvertXmlToPdfDocumentAsync(string tmpFileDownloadName, CancellationToken cancellationToken = default); /// /// Converts an xls file (e.g., .xls, .xlsx file) to a PDF document and saves the result @@ -108,22 +116,24 @@ public interface IConversionService /// /// The temporary file name of the text file to be converted. /// A flag to indicate if only first sheet needs to be converted or all sheets when set to false. + /// The cancellation token used to cancel the service. /// /// A representing the asynchronous operation. The task result is a string containing /// the URL of the saved PDF document, or null if the conversion or save operation fails. /// - Task ConvertXlsToPdfDocumentAsync(string tmpFileDownloadName, bool firstSheetOnly = true); + Task ConvertXlsToPdfDocumentAsync(string tmpFileDownloadName, bool firstSheetOnly = true, CancellationToken cancellationToken = default); /// /// Converts an msg file (e.g., .msg file) to a PDF document and saves the result /// either to local storage or a cloud blob container. /// /// The temporary file name of the text file to be converted. + /// The cancellation token used to cancel the service. /// /// A representing the asynchronous operation. The task result is a string containing /// the URL of the saved PDF document, or null if the conversion or save operation fails. /// - Task ConvertMsgToPdfDocumentAsync(string tmpFileDownloadName); + Task ConvertMsgToPdfDocumentAsync(string tmpFileDownloadName, CancellationToken cancellationToken = default); /// /// Converts an ppt file (e.g., .ppt and pptx file) to a PDF document and saves the result @@ -131,9 +141,10 @@ public interface IConversionService /// /// The temporary file name of the text file to be converted. /// A flag to indicate if only first slide needs to be converted or all slides when set to false.. + /// The cancellation token used to cancel the service. /// /// A representing the asynchronous operation. The task result is a string containing /// the URL of the saved PDF document, or null if the conversion or save operation fails. /// - Task ConvertPptToPdfDocumentAsync(string tmpFileDownloadName, bool firstSlideOnly = true); + Task ConvertPptToPdfDocumentAsync(string tmpFileDownloadName, bool firstSlideOnly = true, CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Interfaces/IDocumentService.cs b/Cps.Fct.Hk.Ui.Interfaces/IDocumentService.cs index a924eed6e..8415117e5 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/IDocumentService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/IDocumentService.cs @@ -20,6 +20,7 @@ public interface IDocumentService /// The file path of the case material document to be downloaded. /// The authentication values used to authorize the request to retrieve the unused materials. /// A flag to indicate if the request is first page only or full document. + /// The cancellation token used to cancel the operation. /// A task that represents the asynchronous operation. The task result contains a object, the downloaded material. - Task GetMaterialDocumentAsync(string caseId, string link, CmsAuthValues cmsAuthValues, bool firstPageOnly = true); + Task GetMaterialDocumentAsync(string caseId, string link, CmsAuthValues cmsAuthValues, bool firstPageOnly = true, CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Interfaces/IMaterialReclassificationOrchestrationService.cs b/Cps.Fct.Hk.Ui.Interfaces/IMaterialReclassificationOrchestrationService.cs index 02de24f7c..ab09bec55 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/IMaterialReclassificationOrchestrationService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/IMaterialReclassificationOrchestrationService.cs @@ -20,10 +20,12 @@ public interface IMaterialReclassificationOrchestrationService /// The material id belonging to the case material to reclassify. /// Authorization values for CMS access. /// The complete reclassification request. + /// The cancellation token used to cancel the operation. /// A representing the result of the asynchronous operation. Task CompleteReclassificationAsync( int caseId, int materialId, CmsAuthValues cmsAuthValues, - CompleteReclassificationRequest request); + CompleteReclassificationRequest request, + CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Interfaces/IReclassificationService.cs b/Cps.Fct.Hk.Ui.Interfaces/IReclassificationService.cs index 528e58003..b65c5beac 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/IReclassificationService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/IReclassificationService.cs @@ -18,16 +18,12 @@ public interface IReclassificationService /// /// Reclassify material to classification type provided. /// - /// The unique identifier of the case. - /// The unique identifier of the material to reclassify. - /// The type of classification to apply. - /// The document type id. - /// Flag to indicate if material is used or unsed. - /// The material subject. + /// The reclassification request containing all material and classification details. /// Authorization values for CMS access. - /// The statement request. - /// Exhibit request. - /// The correspndence identifier. + /// The cancellation token used to cancel the operation. /// A representing the result of the asynchronous operation. - public Task ReclassifyCaseMaterialAsync(int caseId, int materialId, string classification, int documentTypeId, bool used, string subject, CmsAuthValues cmsAuthValues, ReclassifyStatementRequest? statement = null, ReclassifyExhibitRequest? exhibit = null, Guid correspondenceId = default); + public Task ReclassifyCaseMaterialAsync( + ReclassifyCaseMaterialServiceRequest request, + CmsAuthValues cmsAuthValues, + CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Interfaces/IUmaReclassifyService.cs b/Cps.Fct.Hk.Ui.Interfaces/IUmaReclassifyService.cs index a5f9c1d72..0e6fe19f6 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/IUmaReclassifyService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/IUmaReclassifyService.cs @@ -17,9 +17,10 @@ public interface IUmaReclassifyService /// /// the case Id associated with the communications. /// The collection of communications to be matched. + /// The cancellation token used to cancel the operation. /// /// A task representing the asynchronous operation, /// containing a read-only collection of matched communications. /// - Task> ProcessMatchingRequest(int caseId, IReadOnlyCollection communications); + Task> ProcessMatchingRequest(int caseId, IReadOnlyCollection communications, CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Interfaces/IWitnessService.cs b/Cps.Fct.Hk.Ui.Interfaces/IWitnessService.cs index 7ca0c6296..f09f2b738 100644 --- a/Cps.Fct.Hk.Ui.Interfaces/IWitnessService.cs +++ b/Cps.Fct.Hk.Ui.Interfaces/IWitnessService.cs @@ -18,16 +18,18 @@ public interface IWitnessService /// /// Unique identifier of the case to retrieve witnesses for. /// The authentication values used to authorize the request to retrieve witnesses. + /// The cancellation token used to cancel the operation. /// A task representing the asynchronous operation. - Task GetCaseWitnessesAsync(int caseId, CmsAuthValues cmsAuthValues); + Task GetCaseWitnessesAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Service that retrieves statements for a witness. /// /// Unique witness identifier to retrieve statements for. /// The authentication values used to authorize the request to retrieve statements for a witness. + /// The cancellation token used to cancel the operation. /// A representing the asynchronous operation. - Task GetWitnessStatementsAsync(int witnessId, CmsAuthValues cmsAuthValues); + Task GetWitnessStatementsAsync(int witnessId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Service that adds a case witness. @@ -38,6 +40,7 @@ public interface IWitnessService /// Witness last name. /// The authentication values used to authorize the request to retrieve statements for a witness. /// correspondenceId. + /// The cancellation token used to cancel the operation. /// The Id of the witness added. - Task AddWitnessAsync(string urn, int caseId, string firstName, string lastName, CmsAuthValues cmsAuthValues, Guid correspondenceId = default); + Task AddWitnessAsync(string urn, int caseId, string firstName, string lastName, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default); } diff --git a/Cps.Fct.Hk.Ui.Services.Tests/BulkSetUnusedServiceTests.cs b/Cps.Fct.Hk.Ui.Services.Tests/BulkSetUnusedServiceTests.cs index e856b82ec..f5035c8d6 100644 --- a/Cps.Fct.Hk.Ui.Services.Tests/BulkSetUnusedServiceTests.cs +++ b/Cps.Fct.Hk.Ui.Services.Tests/BulkSetUnusedServiceTests.cs @@ -55,8 +55,8 @@ public async Task BulkSetUnusedAsync_AllMaterialsReclassified_ReturnsSuccess() // var cmsAuthValues = new CmsAuthValues(); var cmsAuthValues = new CmsAuthValues("cookies", Guid.NewGuid()); - this.mockApiClient.Setup(client => client.ReclassifyCommunicationAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync((ReclassifyCommunicationRequest req, CmsAuthValues _) => + this.mockApiClient.Setup(client => client.ReclassifyCommunicationAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((ReclassifyCommunicationRequest req, CmsAuthValues _, CancellationToken _) => new ReclassificationResponse( new ReclassifyCommunication { Id = req.materialId })); @@ -94,8 +94,8 @@ public async Task BulkSetUnusedAsync_PartialSuccess_ReturnsPartialSuccess() var cmsAuthValues = new CmsAuthValues("cookies", Guid.NewGuid()); #pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types. - this.mockApiClient.Setup(client => client.ReclassifyCommunicationAsync(It.IsAny(), cmsAuthValues)) - .ReturnsAsync((ReclassifyCommunicationRequest request, CmsAuthValues authValues) => + this.mockApiClient.Setup(client => client.ReclassifyCommunicationAsync(It.IsAny(), cmsAuthValues, It.IsAny())) + .ReturnsAsync((ReclassifyCommunicationRequest request, CmsAuthValues authValues, CancellationToken cancellationToken) => { if (request.materialId == 1) { @@ -186,7 +186,7 @@ public async Task BulkSetUnusedAsync_ApiThrowsException_ThrowsException() var cmsAuthValues = new CmsAuthValues("cookies", Guid.NewGuid()); - this.mockApiClient.Setup(client => client.ReclassifyCommunicationAsync(It.IsAny(), cmsAuthValues)) + this.mockApiClient.Setup(client => client.ReclassifyCommunicationAsync(It.IsAny(), cmsAuthValues, It.IsAny())) .ThrowsAsync(exception); // Act diff --git a/Cps.Fct.Hk.Ui.Services.Tests/CaseInfoServiceTests.cs b/Cps.Fct.Hk.Ui.Services.Tests/CaseInfoServiceTests.cs index 8de17f7ab..71f1df0e1 100644 --- a/Cps.Fct.Hk.Ui.Services.Tests/CaseInfoServiceTests.cs +++ b/Cps.Fct.Hk.Ui.Services.Tests/CaseInfoServiceTests.cs @@ -7,6 +7,7 @@ namespace Cps.Fct.Hk.Ui.Services.Tests; using Xunit; using Moq; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Services.Tests.TestUtilities; using DdeiClient.Clients.Interfaces; diff --git a/Cps.Fct.Hk.Ui.Services.Tests/CommunicationServiceTests.cs b/Cps.Fct.Hk.Ui.Services.Tests/CommunicationServiceTests.cs index 19a3b5e2f..65eceb87c 100644 --- a/Cps.Fct.Hk.Ui.Services.Tests/CommunicationServiceTests.cs +++ b/Cps.Fct.Hk.Ui.Services.Tests/CommunicationServiceTests.cs @@ -2862,6 +2862,7 @@ public async Task GetPcdReviewDetailAsync_ShouldPassCancellationToken_ToDownstre // Arrange int caseId = 1234; int pcdId = 5678; + var customCancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token; var cmsAuthValues = new CmsAuthValues( @@ -2878,15 +2879,15 @@ public async Task GetPcdReviewDetailAsync_ShouldPassCancellationToken_ToDownstre var preChargeDecisionOutcome = new ApiClient.PreChargeDecisionOutcome(); this.apiClientMock - .Setup(client => client.GetHistoryEventsAsync(caseId, cmsAuthValues)) + .Setup(client => client.GetHistoryEventsAsync(caseId, cmsAuthValues, It.IsAny())) .ReturnsAsync(historyEvents); this.apiClientMock - .Setup(client => client.GetInitialReviewByHistoryIdAsync(caseId, pcdId, cmsAuthValues, customCancellationToken)) + .Setup(client => client.GetInitialReviewByHistoryIdAsync(caseId, pcdId, cmsAuthValues, It.IsAny())) .ReturnsAsync(pcdAnalysisOutcome); this.apiClientMock - .Setup(client => client.GetPreChargeDecisionByHistoryId(caseId, pcdId + 1, cmsAuthValues)) + .Setup(client => client.GetPreChargeDecisionByHistoryId(caseId, pcdId + 1, cmsAuthValues, It.IsAny())) .ReturnsAsync(preChargeDecisionOutcome); // Act @@ -3261,7 +3262,7 @@ public async Task GetPcdReviewCoreAsync_ShouldPassCancellationToken_ToDownstream // Arrange int caseId = 1234; int pcdId = 5678; - var customCancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token; + var customCancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(20)).Token; var cmsAuthValues = new CmsAuthValues( "cookies", diff --git a/Cps.Fct.Hk.Ui.Services.Tests/Cps.Fct.Hk.Ui.Services.Tests.csproj b/Cps.Fct.Hk.Ui.Services.Tests/Cps.Fct.Hk.Ui.Services.Tests.csproj index dc30b456e..211459016 100644 --- a/Cps.Fct.Hk.Ui.Services.Tests/Cps.Fct.Hk.Ui.Services.Tests.csproj +++ b/Cps.Fct.Hk.Ui.Services.Tests/Cps.Fct.Hk.Ui.Services.Tests.csproj @@ -16,19 +16,19 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Cps.Fct.Hk.Ui.Services.Tests/MaterialReclassificationOrchestrationServiceUnitTests.cs b/Cps.Fct.Hk.Ui.Services.Tests/MaterialReclassificationOrchestrationServiceUnitTests.cs index 798115a6a..b63ce96f4 100644 --- a/Cps.Fct.Hk.Ui.Services.Tests/MaterialReclassificationOrchestrationServiceUnitTests.cs +++ b/Cps.Fct.Hk.Ui.Services.Tests/MaterialReclassificationOrchestrationServiceUnitTests.cs @@ -79,7 +79,7 @@ public async Task CompleteReclassificationAsync_ReclassifyingMaterialToStatement .ReturnsAsync(new NoContentResult()); this.mockReclassificationService - .Setup(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(It.IsAny()); this.mockCommunicationService @@ -136,7 +136,7 @@ public async Task CompleteReclassificationAsync_ReclassifyingMaterialToStatement log.Message != null && log.Message.Contains($"{LoggingConstants.HskUiLogPrefix} executing reclassification for MaterialId: [{materialId}]")); this.mockWitnessService.Verify(x => x.AddWitnessAsync(It.IsAny(), caseId, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); - this.mockReclassificationService.Verify(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); + this.mockReclassificationService.Verify(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); this.mockActionPlanService.Verify(x => x.AddCaseActionPlanAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); this.mockCommunicationService.Verify(x => x.RenameMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); @@ -178,7 +178,7 @@ public async Task CompleteReclassificationAsync_ReclassifyingMaterialToStatement .ThrowsAsync(exception); this.mockReclassificationService - .Setup(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(It.IsAny()); this.mockCommunicationService @@ -231,7 +231,7 @@ public async Task CompleteReclassificationAsync_ReclassifyingMaterialToStatement log.Message != null && log.Message.Contains($"{LoggingConstants.HskUiLogPrefix} executing reclassification for MaterialId: [{materialId}]")); this.mockWitnessService.Verify(x => x.AddWitnessAsync(It.IsAny(), caseId, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); - this.mockReclassificationService.Verify(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); + this.mockReclassificationService.Verify(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); this.mockActionPlanService.Verify(x => x.AddCaseActionPlanAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); this.mockCommunicationService.Verify(x => x.RenameMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); @@ -272,7 +272,7 @@ public async Task CompleteReclassificationAsync_ReclassifyingMaterialToStatement .ThrowsAsync(exception); this.mockReclassificationService - .Setup(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ThrowsAsync(exception); this.mockCommunicationService @@ -325,7 +325,7 @@ public async Task CompleteReclassificationAsync_ReclassifyingMaterialToStatement log.Message != null && log.Message.Contains($"{LoggingConstants.HskUiLogPrefix} reclassification failed for MaterialId: [{materialId}]")); this.mockWitnessService.Verify(x => x.AddWitnessAsync(It.IsAny(), caseId, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); - this.mockReclassificationService.Verify(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); + this.mockReclassificationService.Verify(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); this.mockActionPlanService.Verify(x => x.AddCaseActionPlanAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); this.mockCommunicationService.Verify(x => x.RenameMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); @@ -364,7 +364,7 @@ public async Task CompleteReclassificationAsync_ReclassifyingMaterialToOther_All .ReturnsAsync(new NoContentResult()); this.mockReclassificationService - .Setup(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(It.IsAny()); this.mockCommunicationService @@ -408,7 +408,7 @@ public async Task CompleteReclassificationAsync_ReclassifyingMaterialToOther_All log.Message != null && log.Message.Contains($"{LoggingConstants.HskUiLogPrefix} executing reclassification for MaterialId: [{materialId}]")); this.mockWitnessService.Verify(x => x.AddWitnessAsync(It.IsAny(), caseId, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); - this.mockReclassificationService.Verify(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); + this.mockReclassificationService.Verify(x => x.ReclassifyCaseMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); this.mockActionPlanService.Verify(x => x.AddCaseActionPlanAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); this.mockCommunicationService.Verify(x => x.RenameMaterialAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); diff --git a/Cps.Fct.Hk.Ui.Services/BulkSetUnusedService.cs b/Cps.Fct.Hk.Ui.Services/BulkSetUnusedService.cs index 9bfdf05d4..66a4daccd 100644 --- a/Cps.Fct.Hk.Ui.Services/BulkSetUnusedService.cs +++ b/Cps.Fct.Hk.Ui.Services/BulkSetUnusedService.cs @@ -55,7 +55,7 @@ public static string SanitizeSubject(string subject) } /// - public async Task BulkSetUnusedAsync(int caseId, CmsAuthValues cmsAuthValues, IReadOnlyCollection bulkSetUnusedRequests) + public async Task BulkSetUnusedAsync(int caseId, CmsAuthValues cmsAuthValues, IReadOnlyCollection bulkSetUnusedRequests, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); var reclassifiedMaterials = new ConcurrentDictionary(); @@ -84,7 +84,7 @@ public async Task BulkSetUnusedAsync(int caseId, CmsAuthV try { - ReclassificationResponse response = await this.apiClient.ReclassifyCommunicationAsync(reclassifyCommunicationRequest, cmsAuthValues).ConfigureAwait(false); + ReclassificationResponse response = await this.apiClient.ReclassifyCommunicationAsync(reclassifyCommunicationRequest, cmsAuthValues, cancellationToken); if (response.ReclassifyCommunication.Id.Equals(bulkSetUnusedRequest.materialId)) { diff --git a/Cps.Fct.Hk.Ui.Services/CaseActionPlanService.cs b/Cps.Fct.Hk.Ui.Services/CaseActionPlanService.cs index a097fde59..337cd067b 100644 --- a/Cps.Fct.Hk.Ui.Services/CaseActionPlanService.cs +++ b/Cps.Fct.Hk.Ui.Services/CaseActionPlanService.cs @@ -30,11 +30,11 @@ public class CaseActionPlanService( private readonly IMasterDataServiceClient apiClient = apiClient; /// - public async Task AddCaseActionPlanAsync(string urn, int caseId, AddCaseActionPlanRequest addCaseActionPlanRequest, CmsAuthValues cmsAuthValues) + public async Task AddCaseActionPlanAsync(string urn, int caseId, AddCaseActionPlanRequest addCaseActionPlanRequest, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); - var checkCaeLockStatus = await this.caseLockService.CheckCaseLockAsync(caseId, cmsAuthValues).ConfigureAwait(false); + var checkCaeLockStatus = await this.caseLockService.CheckCaseLockAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); if (checkCaeLockStatus.IsLocked && !checkCaeLockStatus.IsLockedByCurrentUser) { throw new CaseLockedException($"{LoggingConstants.HskUiLogPrefix} Attempting to add case action plan for caseId [{caseIdString}] failed as case is locked by [{checkCaeLockStatus.LockedByUser}]."); @@ -78,7 +78,7 @@ public async Task AddCaseActionPlanAsync(string urn, int caseId defendantId: addCaseActionPlanRequest.defendantId, steps: mappedSteps!); - NoContentResult addCaseActionPlanResponse = await this.apiClient.AddCaseActionPlanAsync(caseId, request, cmsAuthValues).ConfigureAwait(false); + NoContentResult addCaseActionPlanResponse = await this.apiClient.AddCaseActionPlanAsync(caseId, request, cmsAuthValues, cancellationToken).ConfigureAwait(false); this.logger.LogInformation(LoggingConstants.AddCaseActionPlanOperationSuccess, LoggingConstants.HskUiLogPrefix, caseIdString); diff --git a/Cps.Fct.Hk.Ui.Services/CaseDefendantsService.cs b/Cps.Fct.Hk.Ui.Services/CaseDefendantsService.cs index 00dbbf6cb..b44db8b97 100644 --- a/Cps.Fct.Hk.Ui.Services/CaseDefendantsService.cs +++ b/Cps.Fct.Hk.Ui.Services/CaseDefendantsService.cs @@ -27,7 +27,7 @@ public class CaseDefendantsService( private readonly IMasterDataServiceClient apiClient = apiClient; /// - public async Task GetCaseDefendantsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetCaseDefendantsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -37,7 +37,7 @@ public async Task GetCaseDefendantsAsync(int caseId, CmsAuth var request = new ListCaseDefendantsRequest(caseId, Guid.NewGuid()); - DefendantsResponse? defendants = await this.apiClient.GetCaseDefendantsAsync(request, cmsAuthValues).ConfigureAwait(false); + DefendantsResponse? defendants = await this.apiClient.GetCaseDefendantsAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); return defendants ?? throw new InvalidOperationException($"{LoggingConstants.HskUiLogPrefix} No case defendants found for caseId [{caseIdString}]"); } diff --git a/Cps.Fct.Hk.Ui.Services/CaseInfoService.cs b/Cps.Fct.Hk.Ui.Services/CaseInfoService.cs index 6dd32fe98..6a274243c 100644 --- a/Cps.Fct.Hk.Ui.Services/CaseInfoService.cs +++ b/Cps.Fct.Hk.Ui.Services/CaseInfoService.cs @@ -1,10 +1,12 @@ // +// // Copyright (c) The Crown Prosecution Service. All rights reserved. // namespace Cps.Fct.Hk.Ui.Services; using System; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using System.Globalization; @@ -27,7 +29,7 @@ public class CaseInfoService( private readonly IMasterDataServiceClient mdsClient = mdsClient; /// - public async Task GetCaseInfoAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetCaseInfoAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -37,7 +39,7 @@ public async Task GetCaseInfoAsync(int caseId, CmsAuthValue var request = new GetCaseSummaryRequest(caseId, Guid.NewGuid()); - CaseSummaryResponse? caseSummary = await this.mdsClient.GetCaseSummaryAsync(request, cmsAuthValues).ConfigureAwait(false); + CaseSummaryResponse? caseSummary = await this.mdsClient.GetCaseSummaryAsync(request, cmsAuthValues, cancellationToken); return caseSummary ?? throw new InvalidOperationException($"{LoggingConstants.HskUiLogPrefix} No case summary found for caseId [{caseIdString}]"); } diff --git a/Cps.Fct.Hk.Ui.Services/CaseLockService.cs b/Cps.Fct.Hk.Ui.Services/CaseLockService.cs index b54fbf25f..c0747f352 100644 --- a/Cps.Fct.Hk.Ui.Services/CaseLockService.cs +++ b/Cps.Fct.Hk.Ui.Services/CaseLockService.cs @@ -27,7 +27,7 @@ public class CaseLockService( private readonly IMasterDataServiceClient apiClient = apiClient; /// - public async Task CheckCaseLockAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task CheckCaseLockAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -35,7 +35,7 @@ public async Task CheckCaseLockAsync(int caseId, CmsAuth { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Attempting to check case lock status for caseId [{caseIdString}]"); - var checkCaseLockStatusResponse = await this.apiClient.CheckCaseLockAsync(caseId, cmsAuthValues).ConfigureAwait(false); + var checkCaseLockStatusResponse = await this.apiClient.CheckCaseLockAsync(caseId, cmsAuthValues, cancellationToken); this.logger.LogInformation(LoggingConstants.CheckCaseLockOperationSuccess, LoggingConstants.HskUiLogPrefix, caseIdString); diff --git a/Cps.Fct.Hk.Ui.Services/CaseMaterialService.cs b/Cps.Fct.Hk.Ui.Services/CaseMaterialService.cs index e4442e6a2..7381836c7 100644 --- a/Cps.Fct.Hk.Ui.Services/CaseMaterialService.cs +++ b/Cps.Fct.Hk.Ui.Services/CaseMaterialService.cs @@ -356,15 +356,15 @@ public List MapUnusedMaterialsToCaseMaterials(UnusedMaterialsRespo UsedExhibitsResponse, UsedMgFormsResponse, UsedOtherMaterialsResponse, - ExhibitProducersResponse)> RetrieveCaseMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues) + ExhibitProducersResponse)> RetrieveCaseMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { - var communicationsTask = Task.Run(() => this.GetCommunicationsAsync(caseId, cmsAuthValues)); - var unusedMaterialsTask = Task.Run(() => this.GetUnusedMaterialsAsync(caseId, cmsAuthValues)); - var usedStatementsTask = Task.Run(() => this.GetUsedStatementsAsync(caseId, cmsAuthValues)); - var usedExhibitsTask = Task.Run(() => this.GetUsedExhibitsAsync(caseId, cmsAuthValues)); - var usedMgFormsTask = Task.Run(() => this.GetUsedMgFormsAsync(caseId, cmsAuthValues)); - var usedOtherMaterialsTask = Task.Run(() => this.GetUsedOtherMaterialsAsync(caseId, cmsAuthValues)); - var exhibitProducerTask = Task.Run(() => this.GetExhibitProducersAsync(caseId, cmsAuthValues)); + var communicationsTask = this.GetCommunicationsAsync(caseId, cmsAuthValues, cancellationToken); + var unusedMaterialsTask = this.GetUnusedMaterialsAsync(caseId, cmsAuthValues, cancellationToken); + var usedStatementsTask = this.GetUsedStatementsAsync(caseId, cmsAuthValues, cancellationToken); + var usedExhibitsTask = this.GetUsedExhibitsAsync(caseId, cmsAuthValues, cancellationToken); + var usedMgFormsTask = this.GetUsedMgFormsAsync(caseId, cmsAuthValues, cancellationToken); + var usedOtherMaterialsTask = this.GetUsedOtherMaterialsAsync(caseId, cmsAuthValues, cancellationToken); + var exhibitProducerTask = this.GetExhibitProducersAsync(caseId, cmsAuthValues, cancellationToken); await Task .WhenAll( @@ -392,13 +392,14 @@ await usedOtherMaterialsTask.ConfigureAwait(false), /// /// The ID of the case. /// The authorization values. + /// The cancellation token to cancel this operation. /// A task that represents the asynchronous operation. The task result contains a collection of objects. - public async Task> GetCommunicationsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task> GetCommunicationsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { try { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Retrieving communications for caseId [{caseId}]..."); - return await this.communicationService.GetCommunicationsAsync(caseId, cmsAuthValues).ConfigureAwait(false); + return await this.communicationService.GetCommunicationsAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -413,7 +414,7 @@ public async Task> GetCommunicationsAsync(int /// The ID of the case. /// The authorization values. /// A task that represents the asynchronous operation. The task result contains the . - public async Task GetUnusedMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetUnusedMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { try { @@ -432,13 +433,14 @@ public async Task GetUnusedMaterialsAsync(int caseId, C /// /// The ID of the case. /// The authorization values. + /// The cancellation token. /// A task that represents the asynchronous operation. The task result contains the . - public async Task GetUsedStatementsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetUsedStatementsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { try { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Retrieving used statements for caseId [{caseId}]..."); - return await this.communicationService.GetUsedStatementsAsync(caseId, cmsAuthValues).ConfigureAwait(false); + return await this.communicationService.GetUsedStatementsAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -452,13 +454,14 @@ public async Task GetUsedStatementsAsync(int caseId, Cms /// /// The ID of the case. /// The authorization values. + /// The cancellation token. /// A task that represents the asynchronous operation. The task result contains the . - public async Task GetUsedExhibitsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetUsedExhibitsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { try { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Retrieving used exhibits for caseId [{caseId}]..."); - return await this.communicationService.GetUsedExhibitsAsync(caseId, cmsAuthValues).ConfigureAwait(false); + return await this.communicationService.GetUsedExhibitsAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -472,8 +475,9 @@ public async Task GetUsedExhibitsAsync(int caseId, CmsAuth /// /// The ID of the case. /// The authorization values. + /// The cancellation token. /// A task that represents the asynchronous operation. The task result contains the . - public async Task GetUsedMgFormsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetUsedMgFormsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { try { @@ -492,13 +496,14 @@ public async Task GetUsedMgFormsAsync(int caseId, CmsAuthVa /// /// The ID of the case. /// The authorization values. + /// The cancellation token. /// A task that represents the asynchronous operation. The task result contains the . - public async Task GetUsedOtherMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetUsedOtherMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { try { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Retrieving used other materials for caseId [{caseId}]..."); - return await this.communicationService.GetUsedOtherMaterialsAsync(caseId, cmsAuthValues).ConfigureAwait(false); + return await this.communicationService.GetUsedOtherMaterialsAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -571,14 +576,15 @@ public void AddCaseMaterials(List allCaseMaterials, IEnumerable /// Asynchronously retrieves exhibit producers based on the case ID and authorization values. /// /// The ID of the case. - /// The authorization values.t + /// The authorization values. + /// The cancellation token. /// A task that represents the asynchronous operation. The task result contains the . - public async Task GetExhibitProducersAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetExhibitProducersAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { try { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Retrieving exhibit producers for caseId [{caseId}]..."); - return await this.communicationService.GetExhibitProducersAsync(caseId, cmsAuthValues).ConfigureAwait(false); + return await this.communicationService.GetExhibitProducersAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { diff --git a/Cps.Fct.Hk.Ui.Services/CommunicationService.cs b/Cps.Fct.Hk.Ui.Services/CommunicationService.cs index 32b67d5a3..54bf37ded 100644 --- a/Cps.Fct.Hk.Ui.Services/CommunicationService.cs +++ b/Cps.Fct.Hk.Ui.Services/CommunicationService.cs @@ -38,7 +38,7 @@ public class CommunicationService( private readonly ICommunicationMapper communicationMapper = communicationMapper; /// - public async Task> GetCommunicationsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task> GetCommunicationsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -47,7 +47,7 @@ public async Task> GetCommunicationsAsync(int this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Fetching inbox communications for caseId [{caseIdString}] ..."); var request = new ListCommunicationsHkRequest(caseId, Guid.NewGuid()); - IReadOnlyCollection communications = await this.apiClient.ListCommunicationsHkAsync(request, cmsAuthValues).ConfigureAwait(false); + IReadOnlyCollection communications = await this.apiClient.ListCommunicationsHkAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); // Map the document type to each communication var mappedCommunications = communications.Select(c => @@ -83,7 +83,7 @@ public async Task> GetCommunicationsAsync(int } /// - public async Task GetUsedStatementsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetUsedStatementsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -92,7 +92,7 @@ public async Task GetUsedStatementsAsync(int caseId, Cms this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Fetching used statements for caseId [{caseIdString}] ..."); var request = new GetUsedStatementsRequest(caseId, Guid.NewGuid()); - UsedStatementsResponse usedStatements = await this.apiClient.GetUsedStatementsAsync(request, cmsAuthValues).ConfigureAwait(false); + UsedStatementsResponse usedStatements = await this.apiClient.GetUsedStatementsAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); return usedStatements; } @@ -105,7 +105,7 @@ public async Task GetUsedStatementsAsync(int caseId, Cms } /// - public async Task GetUsedExhibitsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetUsedExhibitsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -114,7 +114,7 @@ public async Task GetUsedExhibitsAsync(int caseId, CmsAuth this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Fetching used exhibits for caseId [{caseIdString}] ..."); var request = new GetUsedExhibitsRequest(caseId, Guid.NewGuid()); - UsedExhibitsResponse usedExhibits = await this.apiClient.GetUsedExhibitsAsync(request, cmsAuthValues).ConfigureAwait(false); + UsedExhibitsResponse usedExhibits = await this.apiClient.GetUsedExhibitsAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); return usedExhibits; } @@ -127,16 +127,17 @@ public async Task GetUsedExhibitsAsync(int caseId, CmsAuth } /// - public async Task GetUsedMgFormsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetUsedMgFormsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); try { + this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Fetching used MG forms for caseId [{caseIdString}] ..."); var request = new GetUsedMgFormsRequest(caseId, Guid.NewGuid()); - UsedMgFormsResponse usedMgForms = await this.apiClient.GetUsedMgFormsAsync(request, cmsAuthValues).ConfigureAwait(false); + UsedMgFormsResponse usedMgForms = await this.apiClient.GetUsedMgFormsAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); return usedMgForms; } @@ -149,7 +150,7 @@ public async Task GetUsedMgFormsAsync(int caseId, CmsAuthVa } /// - public async Task GetUsedOtherMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetUsedOtherMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -158,7 +159,7 @@ public async Task GetUsedOtherMaterialsAsync(int cas this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Fetching used other materials for caseId [{caseIdString}] ..."); var request = new GetUsedOtherMaterialsRequest(caseId, Guid.NewGuid()); - UsedOtherMaterialsResponse usedOtherMaterials = await this.apiClient.GetUsedOtherMaterialsAsync(request, cmsAuthValues).ConfigureAwait(false); + UsedOtherMaterialsResponse usedOtherMaterials = await this.apiClient.GetUsedOtherMaterialsAsync(request, cmsAuthValues, cancellationToken); return usedOtherMaterials; } @@ -171,7 +172,7 @@ public async Task GetUsedOtherMaterialsAsync(int cas } /// - public async Task GetUnusedMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetUnusedMaterialsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -180,7 +181,7 @@ public async Task GetUnusedMaterialsAsync(int caseId, C this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Fetching unused materials for caseId [{caseIdString}] ..."); var request = new GetUnusedMaterialsRequest(caseId, Guid.NewGuid()); - UnusedMaterialsResponse unusedMaterials = await this.apiClient.GetUnusedMaterialsAsync(request, cmsAuthValues).ConfigureAwait(false); + UnusedMaterialsResponse unusedMaterials = await this.apiClient.GetUnusedMaterialsAsync(request, cmsAuthValues, cancellationToken); return unusedMaterials; } @@ -193,7 +194,7 @@ public async Task GetUnusedMaterialsAsync(int caseId, C } /// - public async Task GetAttachmentsAsync(int communicationId, string communicationSubject, CmsAuthValues cmsAuthValues) + public async Task GetAttachmentsAsync(int communicationId, string communicationSubject, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string communicationIdString = communicationId.ToString(CultureInfo.InvariantCulture); @@ -202,7 +203,7 @@ public async Task GetAttachmentsAsync(int communicationId, this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Fetching attachments for communicationId [{communicationIdString}] ..."); var request = new GetAttachmentsRequest(communicationId, Guid.NewGuid()); - AttachmentsResponse attachments = await this.apiClient.GetAttachmentsAsync(request, cmsAuthValues).ConfigureAwait(false); + AttachmentsResponse attachments = await this.apiClient.GetAttachmentsAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); return attachments; } @@ -215,9 +216,7 @@ public async Task GetAttachmentsAsync(int communicationId, } /// - public async Task> RetrieveAllAttachmentsAsync( - IReadOnlyCollection communications, - CmsAuthValues cmsAuthValues) + public async Task> RetrieveAllAttachmentsAsync(IReadOnlyCollection communications, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { try { @@ -228,7 +227,7 @@ public async Task> RetrieveAllAttachmentsAsync( if (communication.HasAttachments) { // Retrieve attachments for the current communication - AttachmentsResponse currentAttachments = await this.GetAttachmentsAsync(communication.Id, communication.Subject, cmsAuthValues).ConfigureAwait(false); + AttachmentsResponse currentAttachments = await this.GetAttachmentsAsync(communication.Id, communication.Subject, cmsAuthValues, cancellationToken).ConfigureAwait(false); if (currentAttachments?.Attachments != null) { @@ -282,7 +281,7 @@ public List MapAttachmentsToCommunications(List attac } /// - public async Task GetCaseMaterialLinkAsync(int caseId, int materialId, CmsAuthValues cmsAuthValues) + public async Task GetCaseMaterialLinkAsync(int caseId, int materialId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { try { @@ -290,12 +289,12 @@ IActionResult GetNotFoundResult() => new NotFoundObjectResult($"{LoggingConstants.HskUiLogPrefix} No case material document found for materialId [{materialId}]"); // Fire off all tasks concurrently - Task> communicationsTask = this.GetCommunicationsAsync(caseId, cmsAuthValues); - Task unusedMaterialsTask = this.GetUnusedMaterialsAsync(caseId, cmsAuthValues); - Task usedStatementsTask = this.GetUsedStatementsAsync(caseId, cmsAuthValues); - Task usedExhibitsTask = this.GetUsedExhibitsAsync(caseId, cmsAuthValues); - Task usedMgFormsTask = this.GetUsedMgFormsAsync(caseId, cmsAuthValues); - Task usedOtherMaterialsTask = this.GetUsedOtherMaterialsAsync(caseId, cmsAuthValues); + Task> communicationsTask = this.GetCommunicationsAsync(caseId, cmsAuthValues, cancellationToken); + Task unusedMaterialsTask = this.GetUnusedMaterialsAsync(caseId, cmsAuthValues, cancellationToken); + Task usedStatementsTask = this.GetUsedStatementsAsync(caseId, cmsAuthValues, cancellationToken); + Task usedExhibitsTask = this.GetUsedExhibitsAsync(caseId, cmsAuthValues, cancellationToken); + Task usedMgFormsTask = this.GetUsedMgFormsAsync(caseId, cmsAuthValues, cancellationToken); + Task usedOtherMaterialsTask = this.GetUsedOtherMaterialsAsync(caseId, cmsAuthValues, cancellationToken); await Task .WhenAll( @@ -363,7 +362,7 @@ await Task } /// - public async Task GetExhibitProducersAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetExhibitProducersAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -373,7 +372,7 @@ public async Task GetExhibitProducersAsync(int caseId, var request = new GetExhibitProducersRequest(caseId, Guid.NewGuid()); - ExhibitProducersResponse? producers = await this.apiClient.GetExhibitProducersAsync(request, cmsAuthValues).ConfigureAwait(false); + ExhibitProducersResponse? producers = await this.apiClient.GetExhibitProducersAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); return producers; } @@ -386,14 +385,14 @@ public async Task GetExhibitProducersAsync(int caseId, } /// - public async Task RenameMaterialAsync(int caseId, int materialId, string subject, CmsAuthValues cmsAuthValues, Guid correspondenceId = default) + public async Task RenameMaterialAsync(int caseId, int materialId, string subject, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default) { try { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Attempting to rename material with materialId [{materialId}]"); var request = new RenameMaterialRequest(correspondenceId == default ? Guid.NewGuid() : correspondenceId, materialId, subject); - RenameMaterialResponse renameMaterialResponse = await this.apiClient.RenameMaterialAsync(request, cmsAuthValues).ConfigureAwait(false); + RenameMaterialResponse renameMaterialResponse = await this.apiClient.RenameMaterialAsync(request, cmsAuthValues, cancellationToken); this.logger.LogInformation(LoggingConstants.RenameMaterialOperationSuccess, LoggingConstants.HskUiLogPrefix, caseId, materialId); return renameMaterialResponse; @@ -407,7 +406,7 @@ public async Task RenameMaterialAsync(int caseId, int ma } /// - public async Task> GetPcdRequestCore(int caseId, CmsAuthValues cmsAuthValues) + public async Task> GetPcdRequestCore(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -417,7 +416,7 @@ public async Task> GetPcdRequestCore(int cas Guid correlationId = cmsAuthValues.CorrelationId == Guid.Empty ? Guid.NewGuid() : cmsAuthValues.CorrelationId; var request = new GetPcdRequestsCoreRequest(caseId, correlationId); - IReadOnlyCollection unusedMaterials = await this.apiClient.GetPcdRequestCoreAsync(request, cmsAuthValues).ConfigureAwait(false); + IReadOnlyCollection unusedMaterials = await this.apiClient.GetPcdRequestCoreAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); return unusedMaterials; } @@ -430,7 +429,7 @@ public async Task> GetPcdRequestCore(int cas } /// - public async Task GetPcdRequestByPcdIdAsync(int caseId, int pcdId, CmsAuthValues cmsAuthValues) + public async Task GetPcdRequestByPcdIdAsync(int caseId, int pcdId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -439,7 +438,7 @@ public async Task GetPcdRequestByPcdIdAsync(int caseId, int pcdId this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Fetching PCD Request overview for caseId [{caseIdString}] and PCD ID [{pcdId}]."); Guid correlationId = cmsAuthValues.CorrelationId == Guid.Empty ? Guid.NewGuid() : cmsAuthValues.CorrelationId; var request = new GetPcdRequestByPcdIdCoreRequest(caseId, pcdId, correlationId); - PcdRequestDto unusedMaterials = await this.apiClient.GetPcdRequestByPcdIdAsync(request, cmsAuthValues).ConfigureAwait(false); + PcdRequestDto unusedMaterials = await this.apiClient.GetPcdRequestByPcdIdAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); return unusedMaterials; } @@ -454,10 +453,11 @@ public async Task GetPcdRequestByPcdIdAsync(int caseId, int pcdId /// public async Task GetPcdReviewDetailAsync(int caseId, int pcdId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken) { + try { this.logger.LogInformation("{HskUiLogPrefix} Fetching case history events with caseId {CaseId}.", LoggingConstants.HskUiLogPrefix, caseId); - var caseHistoryEvents = await this.apiClient.GetHistoryEventsAsync(caseId, cmsAuthValues).ConfigureAwait(false); + var caseHistoryEvents = await this.apiClient.GetHistoryEventsAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); var pcdAnalysisDetails = new PcdReviewDetailResponse(); var associatedPreChargeDecisionEvent = caseHistoryEvents @@ -486,7 +486,7 @@ public async Task GetPcdReviewDetailAsync(int caseId, i if (associatedPreChargeDecisionEvent != null) { this.logger.LogInformation("{HskUiLogPrefix} Fetching pre charge decision outcome for caseId {CaseId} and PCD ID {PcdId}.", LoggingConstants.HskUiLogPrefix, caseId, pcdId); - var preChargeDecisionAnalysisOutcome = await this.apiClient.GetPreChargeDecisionByHistoryId(caseId, (int)associatedPreChargeDecisionEvent.Id, cmsAuthValues).ConfigureAwait(false); + var preChargeDecisionAnalysisOutcome = await this.apiClient.GetPreChargeDecisionByHistoryId(caseId, (int)associatedPreChargeDecisionEvent.Id, cmsAuthValues, cancellationToken).ConfigureAwait(false); pcdAnalysisDetails.PreChargeDecisionOutcome = preChargeDecisionAnalysisOutcome; } @@ -500,12 +500,12 @@ public async Task GetPcdReviewDetailAsync(int caseId, i } /// - public async Task> GetPcdReviewCoreAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken) + public async Task> GetPcdReviewCoreAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { try { this.logger.LogInformation("{HskUiLogPrefix} Fetching case history events with caseId {CaseId}.", LoggingConstants.HskUiLogPrefix, caseId); - var caseHistoryEvents = await this.apiClient.GetHistoryEventsAsync(caseId, cmsAuthValues).ConfigureAwait(false); + var caseHistoryEvents = await this.apiClient.GetHistoryEventsAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); var pcdAnalysisDetails = new PcdReviewDetailResponse(); var associatedPreChargeDecisionEvent = caseHistoryEvents @@ -535,14 +535,14 @@ public async Task> GetPcdReviewCor } /// - public async Task DiscardMaterialAsync(int caseId, int materialId, string discardReason, string discardReasonDescription, CmsAuthValues cmsAuthValues, Guid correspondenceId = default) + public async Task DiscardMaterialAsync(int caseId, int materialId, string discardReason, string discardReasonDescription, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default) { try { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Attempting to discard material with materialId [{materialId}]"); var request = new DiscardMaterialRequest(correspondenceId == default ? Guid.NewGuid() : correspondenceId, materialId, discardReason, discardReasonDescription); - DiscardMaterialResponse discardMaterialResponse = await this.apiClient.DiscardMaterialAsync(request, cmsAuthValues).ConfigureAwait(false); + DiscardMaterialResponse discardMaterialResponse = await this.apiClient.DiscardMaterialAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); this.logger.LogInformation(LoggingConstants.DiscardMaterialOperationSuccess, LoggingConstants.HskUiLogPrefix, caseId, materialId); return discardMaterialResponse; @@ -556,14 +556,15 @@ public async Task DiscardMaterialAsync(int caseId, int } /// - public async Task SetMaterialReadStatusAsync(int materialId, MaterialReadStatusType action, CmsAuthValues cmsAuthValues, Guid correspondenceId = default) + public async Task SetMaterialReadStatusAsync(int materialId, MaterialReadStatusType action, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); try { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Attempting to mark read/unread state of material with material id [{materialId}]"); var request = new SetMaterialReadStatusRequest(correspondenceId == default ? Guid.NewGuid() : correspondenceId, materialId, action); - SetMaterialReadStatusResponse renameMaterialResponse = await this.apiClient.SetMaterialReadStatusAsync(request, cmsAuthValues).ConfigureAwait(false); + SetMaterialReadStatusResponse renameMaterialResponse = await this.apiClient.SetMaterialReadStatusAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Successfully marked material read/unread state of material with material id [{materialId}]"); return renameMaterialResponse; @@ -577,8 +578,9 @@ public async Task SetMaterialReadStatusAsync(int } /// - public async Task UpdateExhibitAsync(int caseId, UpdateExhibitRequest exhibit, CmsAuthValues cmsAuthValues, Guid correspondenceId = default) + public async Task UpdateExhibitAsync(int caseId, UpdateExhibitRequest exhibit, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); try { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Attempting to update exhibit with materialId [{exhibit.MaterialId}]"); @@ -595,7 +597,7 @@ public async Task UpdateExhibitAsync(int caseId, UpdateEx exhibit.NewProducer, exhibit.ExistingProducerOrWitnessId); - UpdateExhibitResponse response = await this.apiClient.UpdateExhibitAsync(request, cmsAuthValues).ConfigureAwait(false); + UpdateExhibitResponse response = await this.apiClient.UpdateExhibitAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); this.logger.LogInformation(LoggingConstants.UpdateExhibitOperationSuccess, LoggingConstants.HskUiLogPrefix, caseId, exhibit.MaterialId); return response; @@ -609,10 +611,11 @@ public async Task UpdateExhibitAsync(int caseId, UpdateEx } /// - public async Task UpdateStatementAsync(int caseId, UpdateStatementRequest statement, CmsAuthValues cmsAuthValues, Guid correspondenceId = default) + public async Task UpdateStatementAsync(int caseId, UpdateStatementRequest statement, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default) { try { + cancellationToken.ThrowIfCancellationRequested(); this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Attempting to update statement with materialId [{statement.MaterialId}]"); var request = new UpdateStatementRequest( @@ -624,7 +627,7 @@ public async Task UpdateStatementAsync(int caseId, Upda statement.StatementNumber, statement.Used); - UpdateStatementResponse response = await this.apiClient.UpdateStatementAsync(request, cmsAuthValues).ConfigureAwait(false); + UpdateStatementResponse response = await this.apiClient.UpdateStatementAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); this.logger.LogInformation(LoggingConstants.UpdateStatementOperationSuccess, LoggingConstants.HskUiLogPrefix, caseId, statement.MaterialId); return response; @@ -638,11 +641,12 @@ public async Task UpdateStatementAsync(int caseId, Upda } /// - public async Task FirstInitialReviewGetCaseHistoryAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task FirstInitialReviewGetCaseHistoryAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); try { - return await this.apiClient.FirstInitialReviewGetCaseHistoryAsync(caseId, cmsAuthValues).ConfigureAwait(false); + return await this.apiClient.FirstInitialReviewGetCaseHistoryAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -654,6 +658,7 @@ public async Task UpdateStatementAsync(int caseId, Upda /// public async Task GetInitialReviewByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); try { return await this.apiClient.GetInitialReviewByHistoryIdAsync(caseId, historyId, cmsAuthValues, cancellationToken); @@ -666,11 +671,12 @@ public async Task UpdateStatementAsync(int caseId, Upda } /// - public async Task> GetHistoryEventsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task> GetHistoryEventsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); try { - return await this.apiClient.GetHistoryEventsAsync(caseId, cmsAuthValues); + return await this.apiClient.GetHistoryEventsAsync(caseId, cmsAuthValues, cancellationToken); } catch (Exception ex) { @@ -680,11 +686,12 @@ public async Task UpdateStatementAsync(int caseId, Upda } /// - public async Task GetOffenceChargeByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues) + public async Task GetOffenceChargeByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { + try { - return await this.apiClient.GetOffenceChargeByHistoryIdAsync(caseId, historyId, cmsAuthValues); + return await this.apiClient.GetOffenceChargeByHistoryIdAsync(caseId, historyId, cmsAuthValues, cancellationToken); } catch (Exception ex) { @@ -694,11 +701,12 @@ public async Task UpdateStatementAsync(int caseId, Upda } /// - public async Task GetPreChargeDecisionCaseHistoryEventDetailsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetPreChargeDecisionCaseHistoryEventDetailsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { + try { - return await this.apiClient.GetPreChargeDecisionCaseHistoryEventDetailsAsync(caseId, cmsAuthValues); + return await this.apiClient.GetPreChargeDecisionCaseHistoryEventDetailsAsync(caseId, cmsAuthValues, cancellationToken); } catch (Exception ex) { @@ -708,11 +716,12 @@ public async Task UpdateStatementAsync(int caseId, Upda } /// - public async Task GetPreChargeDecisionByHistoryId(int caseId, int historyId, CmsAuthValues cmsAuthValues) + public async Task GetPreChargeDecisionByHistoryId(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { + try { - return await this.apiClient.GetPreChargeDecisionByHistoryId(caseId, historyId, cmsAuthValues); + return await this.apiClient.GetPreChargeDecisionByHistoryId(caseId, historyId, cmsAuthValues, cancellationToken); } catch (Exception ex) { @@ -722,11 +731,12 @@ public async Task UpdateStatementAsync(int caseId, Upda } /// - public async Task GetPcdReview(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetPcdReview(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { + try { - return await this.apiClient.GetPcdReview(caseId, cmsAuthValues); + return await this.apiClient.GetPcdReview(caseId, cmsAuthValues, cancellationToken); } catch (Exception ex) { diff --git a/Cps.Fct.Hk.Ui.Services/Constants/CommsDocumentTypeIds.cs b/Cps.Fct.Hk.Ui.Services/Constants/CommsDocumentTypeIds.cs new file mode 100644 index 000000000..910f5d952 --- /dev/null +++ b/Cps.Fct.Hk.Ui.Services/Constants/CommsDocumentTypeIds.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) The Crown Prosecution Service. All rights reserved. +// + +namespace Cps.Fct.Hk.Ui.Services.Constants; + +/// +/// Contains constants for Communication document Type IDs to be excluded from MGForms listings. +/// These IDs correspond to specific document types in the DocumentTypeMapper. +/// +public static class CommsDocumentTypeIds +{ + /// + /// Communication Type ID for PE3 document type. + /// + public const string PE3 = "1053"; + + /// + /// Communication Type ID for PE4 document type. + /// + public const string PE4 = "1054"; + + /// + /// Communication Type ID for DREP document type. + /// + public const string DREP = "1055"; + + /// + /// Communication Type IDs that should be excluded from Used MG Forms. + /// + public static readonly string[] ExcludedFromUsedMgForms = { PE3, PE4, DREP }; +} diff --git a/Cps.Fct.Hk.Ui.Services/ConversionService.cs b/Cps.Fct.Hk.Ui.Services/ConversionService.cs index 7d42c9aec..0d668a154 100644 --- a/Cps.Fct.Hk.Ui.Services/ConversionService.cs +++ b/Cps.Fct.Hk.Ui.Services/ConversionService.cs @@ -35,7 +35,7 @@ public class ConversionService(ILogger logger, private const string PasswordProtectedMessage = "The document is password protected."; /// - public async Task SaveDownloadedDocumentToTemporaryStorageAsync(FileStreamResult downloadedDocument) + public async Task SaveDownloadedDocumentToTemporaryStorageAsync(FileStreamResult downloadedDocument, CancellationToken cancellationToken = default) { string? blobContainerName = this.configuration[StorageKeys.BlobServiceContainerNameDocuments] ?? string.Empty; @@ -55,7 +55,7 @@ public async Task SaveDownloadedDocumentToTemporaryStorageAsync(FileStream BlobContainerClient containerClient = this.blobServiceClient.GetBlobContainerClient(blobContainerName); // Ensure the container exists (create if it does not) - await containerClient.CreateIfNotExistsAsync().ConfigureAwait(true); + await containerClient.CreateIfNotExistsAsync(cancellationToken: cancellationToken).ConfigureAwait(true); // Get a reference to the blob (file) with the temporary name BlobClient blobClient = containerClient.GetBlobClient(temporaryFileName); @@ -70,10 +70,10 @@ public async Task SaveDownloadedDocumentToTemporaryStorageAsync(FileStream return false; } - await downloadedDocument.FileStream.CopyToAsync(memoryStream).ConfigureAwait(false); + await downloadedDocument.FileStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false); memoryStream.Position = 0; // Reset stream position - await blobClient.UploadAsync(memoryStream, overwrite: true).ConfigureAwait(false); + await blobClient.UploadAsync(memoryStream, overwrite: true, cancellationToken).ConfigureAwait(false); } this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Downloaded document saved temporarily to Azure Blob Storage as [{temporaryFileName}]"); @@ -89,10 +89,12 @@ public async Task SaveDownloadedDocumentToTemporaryStorageAsync(FileStream } /// - public async Task ConvertToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true) + public async Task ConvertToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true, CancellationToken cancellationToken = default) { string? pdfFilePath = null; + cancellationToken.ThrowIfCancellationRequested(); + try { // Determine the content type of the file @@ -102,22 +104,22 @@ public async Task SaveDownloadedDocumentToTemporaryStorageAsync(FileStream { pdfFilePath = contentType switch { - var type when this.IsRasterImage(type) => await ConvertToPdfFilePathAsync(() => this.ConvertRasterImageToPdfDocumentAsync(tmpFileDownloadName)).ConfigureAwait(true), - "application/msword" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), // DOC and DOT files - "application/octet-stream" => await ConvertToPdfFilePathAsync(() => this.ConvertHtmlToPdfDocumentAsync(tmpFileDownloadName)).ConfigureAwait(true), // HTE, HTM and HTML files - "application/pdf" => await ConvertToPdfFilePathAsync(() => this.ConvertPdfToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), - "application/rtf" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName)).ConfigureAwait(true), - "application/vnd.ms-excel" => await ConvertToPdfFilePathAsync(() => this.ConvertXlsToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), + var type when this.IsRasterImage(type) => await ConvertToPdfFilePathAsync(() => this.ConvertRasterImageToPdfDocumentAsync(tmpFileDownloadName, cancellationToken)).ConfigureAwait(true), + "application/msword" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly, cancellationToken)).ConfigureAwait(true), // DOC and DOT files + "application/octet-stream" => await ConvertToPdfFilePathAsync(() => this.ConvertHtmlToPdfDocumentAsync(tmpFileDownloadName, cancellationToken)).ConfigureAwait(true), // HTE, HTM and HTML files + "application/pdf" => await ConvertToPdfFilePathAsync(() => this.ConvertPdfToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly, cancellationToken)).ConfigureAwait(true), + "application/rtf" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName, cancellationToken:cancellationToken)).ConfigureAwait(true), + "application/vnd.ms-excel" => await ConvertToPdfFilePathAsync(() => this.ConvertXlsToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly, cancellationToken)).ConfigureAwait(true), "application/vnd.ms-excel.sheet.macroEnabled.12" => await ConvertToPdfFilePathAsync(() => this.ConvertXlsToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), - "application/vnd.ms-word.document.macroEnabled.12" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), - "application/vnd.ms-word.template.macroEnabled.12" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), - "application/vnd.ms-outlook" => await ConvertToPdfFilePathAsync(() => this.ConvertMsgToPdfDocumentAsync(tmpFileDownloadName)).ConfigureAwait(true), - "application/vnd.ms-powerpoint" => await ConvertToPdfFilePathAsync(() => this.ConvertPptToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), + "application/vnd.ms-word.document.macroEnabled.12" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly, cancellationToken:cancellationToken)).ConfigureAwait(true), + "application/vnd.ms-word.template.macroEnabled.12" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly, cancellationToken:cancellationToken)).ConfigureAwait(true), + "application/vnd.ms-outlook" => await ConvertToPdfFilePathAsync(() => this.ConvertMsgToPdfDocumentAsync(tmpFileDownloadName, cancellationToken: cancellationToken)).ConfigureAwait(true), + "application/vnd.ms-powerpoint" => await ConvertToPdfFilePathAsync(() => this.ConvertPptToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly,cancellationToken)).ConfigureAwait(true), "application/vnd.openxmlformats-officedocument.presentationml.presentation" => await ConvertToPdfFilePathAsync(() => this.ConvertPptToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), - "application/vnd.openxmlformats-officedocument.wordprocessingml.document" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), - "application/vnd.openxmlformats-officedocument.wordprocessingml.template" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly, cancellationToken)).ConfigureAwait(true), + "application/vnd.openxmlformats-officedocument.wordprocessingml.template" => await ConvertToPdfFilePathAsync(() => this.ConvertDocToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly, cancellationToken)).ConfigureAwait(true), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" => await ConvertToPdfFilePathAsync(() => this.ConvertXlsToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), - "application/xml" => await ConvertToPdfFilePathAsync(() => this.ConvertXmlToPdfDocumentAsync(tmpFileDownloadName)).ConfigureAwait(true), + "application/xml" => await ConvertToPdfFilePathAsync(() => this.ConvertXmlToPdfDocumentAsync(tmpFileDownloadName, cancellationToken: cancellationToken)).ConfigureAwait(true), "text/csv" => await ConvertToPdfFilePathAsync(() => this.ConvertXlsToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly)).ConfigureAwait(true), "text/plain" => await ConvertToPdfFilePathAsync(() => this.ConvertTxtToPdfDocumentAsync(tmpFileDownloadName)).ConfigureAwait(true), "text/xml" => await ConvertToPdfFilePathAsync(() => this.ConvertXmlToPdfDocumentAsync(tmpFileDownloadName)).ConfigureAwait(true), @@ -142,8 +144,10 @@ var type when this.IsRasterImage(type) => await ConvertToPdfFilePathAsync(() => } /// - public async Task ConvertRasterImageToPdfDocumentAsync(string tmpFileDownloadName) + public async Task ConvertRasterImageToPdfDocumentAsync(string tmpFileDownloadName, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); + string pdfFileName = $"preview_{tmpFileDownloadName}.pdf"; // PDF blob name string? pdfFileUrl = null; // URL to return after upload @@ -199,8 +203,10 @@ var type when this.IsRasterImage(type) => await ConvertToPdfFilePathAsync(() => } /// - public async Task ConvertPdfToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true) + public async Task ConvertPdfToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); + string pdfFileName = $"preview_{tmpFileDownloadName}.pdf"; // PDF output blob name string? pdfFileUrl = null; // URL to return after upload @@ -286,8 +292,10 @@ var type when this.IsRasterImage(type) => await ConvertToPdfFilePathAsync(() => } /// - public async Task ConvertTxtToPdfDocumentAsync(string tmpFileDownloadName) + public async Task ConvertTxtToPdfDocumentAsync(string tmpFileDownloadName, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); + string pdfFileName = $"preview_{tmpFileDownloadName}.pdf"; // Output PDF blob name string? pdfFileUrl = null; // URL to return after upload @@ -355,8 +363,10 @@ var type when this.IsRasterImage(type) => await ConvertToPdfFilePathAsync(() => } /// - public async Task ConvertHtmlToPdfDocumentAsync(string tmpFileDownloadName) + public async Task ConvertHtmlToPdfDocumentAsync(string tmpFileDownloadName, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); + string pdfFileName = $"preview_{tmpFileDownloadName}.pdf"; // PDF output blob name string? pdfFileUrl = null; // URL to return after upload @@ -416,8 +426,10 @@ var type when this.IsRasterImage(type) => await ConvertToPdfFilePathAsync(() => } /// - public async Task ConvertDocToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true) + public async Task ConvertDocToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); + string pdfFileName = $"preview_{tmpFileDownloadName}.pdf"; // PDF output blob name string? pdfFileUrl = null; // URL to return after upload @@ -446,7 +458,7 @@ var type when this.IsRasterImage(type) => await ConvertToPdfFilePathAsync(() => if (document.PageCount > 0) { // Extract pages, convert to PDF, upload to Blob Storage, and get the PDF URL - pdfFileUrl = await ExtractPagesAndUploadPdfAsync(document, containerClient, pdfFileName, convertAllPages); + pdfFileUrl = await ExtractPagesAndUploadPdfAsync(document, containerClient, pdfFileName, convertAllPages, cancellationToken); string firstPageText = !convertAllPages ? "(first page)" : string.Empty; this.logger.LogInformation( @@ -482,8 +494,9 @@ var type when this.IsRasterImage(type) => await ConvertToPdfFilePathAsync(() => } - private static async Task ExtractPagesAndUploadPdfAsync(Aspose.Words.Document document, BlobContainerClient containerClient, string pdfFileName, bool convertAllPages) + private static async Task ExtractPagesAndUploadPdfAsync(Aspose.Words.Document document, BlobContainerClient containerClient, string pdfFileName, bool convertAllPages, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); Aspose.Words.Document wordDocument; if (convertAllPages) { @@ -527,8 +540,9 @@ private static async Task ExtractPagesAndUploadPdfAsync(Aspose.Words.Doc } /// - public async Task ConvertXmlToPdfDocumentAsync(string tmpFileDownloadName) + public async Task ConvertXmlToPdfDocumentAsync(string tmpFileDownloadName, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); string pdfFileName = $"preview_{tmpFileDownloadName}.pdf"; // PDF output blob name string? pdfFileUrl = null; // URL to return after upload @@ -595,8 +609,9 @@ private static async Task ExtractPagesAndUploadPdfAsync(Aspose.Words.Doc } /// - public async Task ConvertXlsToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true) + public async Task ConvertXlsToPdfDocumentAsync(string tmpFileDownloadName, bool firstSheetOnly = true, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); string pdfFileName = $"preview_{tmpFileDownloadName}.pdf"; // Output PDF blob name string? pdfFileUrl = null; // URL to return after upload @@ -620,7 +635,7 @@ private static async Task ExtractPagesAndUploadPdfAsync(Aspose.Words.Doc // Load the XLS file from the downloaded stream using Aspose.Cells var workbook = new Aspose.Cells.Workbook(xlsStream); - var convertAllSheets = firstPageOnly && workbook.Worksheets.Count > 1; + var convertAllSheets = firstSheetOnly && workbook.Worksheets.Count > 1; // Create a PdfSaveOptions object to customize the PDF save behavior var pdfSaveOptions = new Aspose.Cells.PdfSaveOptions(); @@ -672,8 +687,9 @@ private static async Task ExtractPagesAndUploadPdfAsync(Aspose.Words.Doc } /// - public async Task ConvertMsgToPdfDocumentAsync(string tmpFileDownloadName) + public async Task ConvertMsgToPdfDocumentAsync(string tmpFileDownloadName, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); string pdfFileName = $"preview_{tmpFileDownloadName}.pdf"; // Output PDF blob name string? pdfFileUrl = null; // URL to return after upload @@ -735,8 +751,9 @@ private static async Task ExtractPagesAndUploadPdfAsync(Aspose.Words.Doc } /// - public async Task ConvertPptToPdfDocumentAsync(string tmpFileDownloadName, bool firstPageOnly = true) + public async Task ConvertPptToPdfDocumentAsync(string tmpFileDownloadName, bool firstSlideOnly = true, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); string pdfFileName = $"preview_{tmpFileDownloadName}.pdf"; // Output PDF blob name string? pdfFileUrl = null; // URL to return after upload @@ -762,9 +779,9 @@ private static async Task ExtractPagesAndUploadPdfAsync(Aspose.Words.Doc using (var presentation = new Aspose.Slides.Presentation(pptxStream)) { // Create a new presentation to hold the first slide - using (var slidePresentation = new Aspose.Slides.Presentation()) + using (var slidePresentation = new Aspose.Slides.Presentation()) { - convertAllslides = firstPageOnly == false && presentation.Slides.Count > 1; + convertAllslides = firstSlideOnly && presentation.Slides.Count > 1; // Check if there are any slides in the original presentation if (presentation.Slides.Count > 0) diff --git a/Cps.Fct.Hk.Ui.Services/Cps.Fct.Hk.Ui.Services.csproj b/Cps.Fct.Hk.Ui.Services/Cps.Fct.Hk.Ui.Services.csproj index eda5414d7..426d86861 100644 --- a/Cps.Fct.Hk.Ui.Services/Cps.Fct.Hk.Ui.Services.csproj +++ b/Cps.Fct.Hk.Ui.Services/Cps.Fct.Hk.Ui.Services.csproj @@ -16,8 +16,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -26,7 +26,7 @@ - + diff --git a/Cps.Fct.Hk.Ui.Services/DocumentService.cs b/Cps.Fct.Hk.Ui.Services/DocumentService.cs index f368e98b7..1f0a00eb7 100644 --- a/Cps.Fct.Hk.Ui.Services/DocumentService.cs +++ b/Cps.Fct.Hk.Ui.Services/DocumentService.cs @@ -39,7 +39,7 @@ public class DocumentService( private readonly BlobServiceClient? blobServiceClient = blobServiceClient; /// - public async Task GetMaterialDocumentAsync(string caseId, string link, CmsAuthValues cmsAuthValues, bool firstPageOnly = true) + public async Task GetMaterialDocumentAsync(string caseId, string link, CmsAuthValues cmsAuthValues, bool firstPageOnly = true, CancellationToken cancellationToken = default) { string? tmpFileDownloadName = null; string? blobContainerName = this.configuration[StorageKeys.BlobServiceContainerNameDocuments] ?? string.Empty; @@ -67,7 +67,7 @@ public class DocumentService( bool tmpSaved; // Save the document to temporary storage (Blob storage in this case) - tmpSaved = await this.conversionService.SaveDownloadedDocumentToTemporaryStorageAsync(downloadedDocument).ConfigureAwait(true); + tmpSaved = await this.conversionService.SaveDownloadedDocumentToTemporaryStorageAsync(downloadedDocument, cancellationToken).ConfigureAwait(true); if (tmpSaved) { @@ -75,7 +75,7 @@ public class DocumentService( string fileDownloadName = $"{tmpFileDownloadName}.pdf"; // Convert the saved document to a PDF and store it in Blob Storage - string? pdfBlobUrl = await this.conversionService.ConvertToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly).ConfigureAwait(true); + string? pdfBlobUrl = await this.conversionService.ConvertToPdfDocumentAsync(tmpFileDownloadName, firstPageOnly, cancellationToken).ConfigureAwait(true); if (pdfBlobUrl == null) { @@ -89,7 +89,7 @@ public class DocumentService( .GetBlobClient($"preview_{tmpFileDownloadName}.pdf"); var pdfStream = new MemoryStream(); - await blobClient.DownloadToAsync(pdfStream).ConfigureAwait(false); + await blobClient.DownloadToAsync(pdfStream, cancellationToken).ConfigureAwait(false); pdfStream.Position = 0; // Reset stream position for reading return new FileStreamResult(pdfStream, "application/pdf") @@ -131,7 +131,7 @@ public class DocumentService( { if (!string.IsNullOrEmpty(blobContainerName)) { - await this.DeleteBlobAsync(blobContainerName, $"tmp_{tmpFileDownloadName}", "Temporary file").ConfigureAwait(false); + await this.DeleteBlobAsync(blobContainerName, $"tmp_{tmpFileDownloadName}", "Temporary file", cancellationToken).ConfigureAwait(false); await this.DeleteBlobAsync(blobContainerName, $"preview_{tmpFileDownloadName}.pdf", "Temporary preview or full document").ConfigureAwait(false); } } @@ -144,10 +144,11 @@ public class DocumentService( /// The name of the blob container where the blob is stored. /// The name of the blob to be deleted. /// A description of the blob (used in log messages). + /// A token to monitor for cancellation requests. /// A task representing the asynchronous operation. /// Thrown if the blobServiceClient is not initialized. /// Thrown if the blob container name is null or empty. - internal async Task DeleteBlobAsync(string blobContainerName, string blobName, string description) + internal async Task DeleteBlobAsync(string blobContainerName, string blobName, string description, CancellationToken cancellationToken = default) { try { @@ -168,7 +169,7 @@ internal async Task DeleteBlobAsync(string blobContainerName, string blobName, s .GetBlobClient(blobName); // Delete the blob - await blobClient.DeleteIfExistsAsync().ConfigureAwait(false); + await blobClient.DeleteIfExistsAsync(cancellationToken: cancellationToken).ConfigureAwait(false); this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} {description} [{blobName}] deleted from Blob Storage."); } diff --git a/Cps.Fct.Hk.Ui.Services/DocumentTypeMapper.cs b/Cps.Fct.Hk.Ui.Services/DocumentTypeMapper.cs index 4f2f2b0ce..b22809b6c 100644 --- a/Cps.Fct.Hk.Ui.Services/DocumentTypeMapper.cs +++ b/Cps.Fct.Hk.Ui.Services/DocumentTypeMapper.cs @@ -87,7 +87,7 @@ public DocumentTypeMapper(ILogger logger) { 1028, new DocumentTypeInfo { DocumentType = "MG/FSSB", Category = DocumentTypeCategories.Exhibit, Group = DocumentTypeGroups.Exhibit } }, { 1029, new DocumentTypeInfo { DocumentType = "Other Communication", Category = DocumentTypeCategories.Communication, Group = DocumentTypeGroups.Other } }, { 1030, new DocumentTypeInfo { DocumentType = "Other Exhibit", Category = DocumentTypeCategories.Exhibit, Group = DocumentTypeGroups.Exhibit } }, - { 1200, new DocumentTypeInfo { DocumentType = "Other Material", Category = DocumentTypeCategories.Communication, Group = DocumentTypeGroups.Other } }, + { 1200, new DocumentTypeInfo { DocumentType = "Other Material", Category = DocumentTypeCategories.OtherMaterial, Group = DocumentTypeGroups.Other } }, { 1056, new DocumentTypeInfo { DocumentType = "PCN1", Category = DocumentTypeCategories.DefendantPreCons, Group = DocumentTypeGroups.Other } }, { 1057, new DocumentTypeInfo { DocumentType = "PCN2", Category = DocumentTypeCategories.DefendantPreCons, Group = DocumentTypeGroups.Other } }, { 1058, new DocumentTypeInfo { DocumentType = "PCN3", Category = DocumentTypeCategories.UnusedMaterial, Group = DocumentTypeGroups.MgForm } }, diff --git a/Cps.Fct.Hk.Ui.Services/MaterialReclassificationOrchestrationService.cs b/Cps.Fct.Hk.Ui.Services/MaterialReclassificationOrchestrationService.cs index 9ffcdb68a..a5802c551 100644 --- a/Cps.Fct.Hk.Ui.Services/MaterialReclassificationOrchestrationService.cs +++ b/Cps.Fct.Hk.Ui.Services/MaterialReclassificationOrchestrationService.cs @@ -38,7 +38,8 @@ public async Task CompleteReclassificationAsyn int caseId, int materialId, CmsAuthValues cmsAuthValues, - CompleteReclassificationRequest request) + CompleteReclassificationRequest request, + CancellationToken cancellationToken = default) { var transactionId = Guid.NewGuid(); var renameMaterialRequest = new RenameMaterialRequest(transactionId, materialId, request.reclassification.subject); @@ -62,7 +63,7 @@ public async Task CompleteReclassificationAsyn // Add witness task must be executed first when user has selected add witness flow, due to reclassification to Statement having a dependency on a new witness id. if (request!.AddWitness()) { - addWitnessResult = await this.ExecuteAddWitness(request.reclassification.urn, caseId, request.witness!, cmsAuthValues, transactionId).ConfigureAwait(false); + addWitnessResult = await this.ExecuteAddWitness(request.reclassification.urn, caseId, request.witness!, cmsAuthValues, transactionId, cancellationToken).ConfigureAwait(false); if (addWitnessResult.Success) { @@ -74,19 +75,19 @@ public async Task CompleteReclassificationAsyn // Add Action plan task to collection of tasks to be run in parallel if (request.HasActionPlan()) { - actionPlanTask = this.ExecuteAddActionPlan(request.reclassification.urn, caseId, request.actionPlan!, cmsAuthValues, transactionId); + actionPlanTask = this.ExecuteAddActionPlan(request.reclassification.urn, caseId, request.actionPlan!, cmsAuthValues, transactionId, cancellationToken); tasksToRun.Add(actionPlanTask); } // Rename material for all other non statement reclassification. if (!request.HasStatement()) { - renameMaterialTask = this.ExecuteMaterialRename(caseId, renameMaterialRequest, cmsAuthValues, transactionId); + renameMaterialTask = this.ExecuteMaterialRename(caseId, renameMaterialRequest, cmsAuthValues, transactionId, cancellationToken); tasksToRun.Add(renameMaterialTask); } // Add reclassification task to collection of tasks to be run in parallel - reclassificationTask = this.ExecuteReclassificationAsync(caseId, materialId, request.reclassification, cmsAuthValues, transactionId); + reclassificationTask = this.ExecuteReclassificationAsync(caseId, materialId, request.reclassification, cmsAuthValues, transactionId, cancellationToken); tasksToRun.Add(reclassificationTask); // Wait for all operations to complete running in parallel. @@ -127,24 +128,28 @@ private async Task ExecuteReclassificationAsync( int materialId, ReclassifyCaseMaterialRequest request, CmsAuthValues cmsAuthValues, - Guid transactionId) + Guid transactionId, + CancellationToken cancellationToken = default) { try { this.logger.LogInformation( $"{LoggingConstants.HskUiLogPrefix} executing reclassification for MaterialId: [{materialId}], TransactionId: {transactionId}"); - // Call the underlying service method directly + var serviceRequest = new ReclassifyCaseMaterialServiceRequest( + CaseId: caseId, + MaterialId: materialId, + Classification: request.classification, + DocumentTypeId: request.documentTypeId, + Used: request.used, + Subject: request.subject, + Statement: request.Statement, + Exhibit: request.Exhibit); + ReclassificationResponse result = await this.reclassificationService.ReclassifyCaseMaterialAsync( - caseId, - materialId, - request.classification, - request.documentTypeId, - request.used, - request.subject, + serviceRequest, cmsAuthValues, - request.Statement, - request.Exhibit).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); return new OperationResult( Success: true, @@ -168,7 +173,8 @@ private async Task ExecuteMaterialRename( int caseId, RenameMaterialRequest request, CmsAuthValues cmsAuthValues, - Guid transactionId) + Guid transactionId, + CancellationToken cancellationToken = default) { try { @@ -176,7 +182,7 @@ private async Task ExecuteMaterialRename( $"{LoggingConstants.HskUiLogPrefix} executing rename for MaterialId: [{request.materialId}], TransactionId: [{transactionId}", request.materialId, request.CorrespondenceId); // Call the underlying service method directly - RenameMaterialResponse result = await this.communicationService.RenameMaterialAsync(caseId, request.materialId, request.subject, cmsAuthValues).ConfigureAwait(false); + RenameMaterialResponse result = await this.communicationService.RenameMaterialAsync(caseId, request.materialId, request.subject, cmsAuthValues, cancellationToken: cancellationToken).ConfigureAwait(false); return new OperationResult( Success: true, @@ -201,14 +207,15 @@ private async Task ExecuteAddActionPlan( int caseId, AddCaseActionPlanRequest request, CmsAuthValues cmsAuthValues, - Guid transactionId) + Guid transactionId, + CancellationToken cancellationToken = default) { try { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} executing action plan for URN: [{urn}], TransactionId: [{transactionId}]"); // Call the underlying service method directly - NoContentResult result = await this.caseActionPlanService.AddCaseActionPlanAsync(urn, caseId, request, cmsAuthValues).ConfigureAwait(false); + NoContentResult result = await this.caseActionPlanService.AddCaseActionPlanAsync(urn, caseId, request, cmsAuthValues, cancellationToken).ConfigureAwait(false); return new OperationResult( Success: true, @@ -233,7 +240,8 @@ private async Task ExecuteAddWitness( int caseId, Common.Dto.Request.HouseKeeping.WitnessRequest request, CmsAuthValues cmsAuthValues, - Guid transactionId) + Guid transactionId, + CancellationToken cancellationToken = default) { try { @@ -245,7 +253,8 @@ private async Task ExecuteAddWitness( request.FirstName, request.Surname, cmsAuthValues, - transactionId).ConfigureAwait(false); + transactionId, + cancellationToken); return new OperationResult( Success: true, diff --git a/Cps.Fct.Hk.Ui.Services/ReclassificationService.cs b/Cps.Fct.Hk.Ui.Services/ReclassificationService.cs index 17446f05e..1113299a9 100644 --- a/Cps.Fct.Hk.Ui.Services/ReclassificationService.cs +++ b/Cps.Fct.Hk.Ui.Services/ReclassificationService.cs @@ -26,38 +26,35 @@ public class ReclassificationService( /// public async Task ReclassifyCaseMaterialAsync( - int caseId, - int materialId, - string classification, - int documentTypeId, - bool used, - string subject, + ReclassifyCaseMaterialServiceRequest request, CmsAuthValues cmsAuthValues, - ReclassifyStatementRequest? statement = null, - ReclassifyExhibitRequest? exhibit = null, - Guid correspondenceId = default) + CancellationToken cancellationToken = default) { try { - this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Attempting to reclassify a case material with materidId [{materialId}] associated with case with caseId [{caseId}]"); + this.logger.LogInformation("{Prefix} Attempting to reclassify a case material with materidId [{MaterialId}] associated with case with caseId [{CaseId}]", LoggingConstants.HskUiLogPrefix, request.MaterialId, request.CaseId); // Handle an exception to the rule for 'Defence statement' category that requires a different classification. - if (documentTypeId == -2) - { - classification = "DEFENCESTATEMENT"; - } + var classification = request.DocumentTypeId == -2 ? "DEFENCESTATEMENT" : request.Classification; - var request = new ReclassifyCommunicationRequest(correspondenceId != default ? correspondenceId : Guid.NewGuid(), classification, materialId, documentTypeId, used, subject, statement, exhibit); + var communicationRequest = new ReclassifyCommunicationRequest( + request.CorrespondenceId != Guid.Empty ? request.CorrespondenceId : Guid.NewGuid(), + classification, + request.MaterialId, + request.DocumentTypeId, + request.Used, + request.Subject, + request.Statement, + request.Exhibit); - ReclassificationResponse reclassificationResponse = await this.apiClient.ReclassifyCommunicationAsync(request, cmsAuthValues).ConfigureAwait(false); - this.logger.LogInformation(LoggingConstants.ReclassifyCaseMaterialOperationSuccess, LoggingConstants.HskUiLogPrefix, caseId, materialId); + ReclassificationResponse reclassificationResponse = await this.apiClient.ReclassifyCommunicationAsync(communicationRequest, cmsAuthValues, cancellationToken).ConfigureAwait(false); + this.logger.LogInformation(LoggingConstants.ReclassifyCaseMaterialOperationSuccess, LoggingConstants.HskUiLogPrefix, request.CaseId, request.MaterialId); return reclassificationResponse; } catch (Exception ex) { - this.logger.LogError(ex, LoggingConstants.ReclassifyCaseMaterialOperationFailed, LoggingConstants.HskUiLogPrefix, caseId, materialId); - this.logger.LogError(ex, ex.Message); + this.logger.LogError(ex, LoggingConstants.ReclassifyCaseMaterialOperationFailed, LoggingConstants.HskUiLogPrefix, request.CaseId, request.MaterialId); throw; } } diff --git a/Cps.Fct.Hk.Ui.Services/UmaReclassifyService.cs b/Cps.Fct.Hk.Ui.Services/UmaReclassifyService.cs index 50b3fef49..2e764e5bd 100644 --- a/Cps.Fct.Hk.Ui.Services/UmaReclassifyService.cs +++ b/Cps.Fct.Hk.Ui.Services/UmaReclassifyService.cs @@ -21,8 +21,9 @@ public class UmaReclassifyService(ILogger logger, IUmaServ private readonly IUmaServiceClient umaClient = umaClient; /// - public async Task> ProcessMatchingRequest(int caseId, IReadOnlyCollection communications) + public async Task> ProcessMatchingRequest(int caseId, IReadOnlyCollection communications, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); ArgumentNullException.ThrowIfNull(communications); try diff --git a/Cps.Fct.Hk.Ui.Services/WitnessService.cs b/Cps.Fct.Hk.Ui.Services/WitnessService.cs index 47340e842..ba0614b8c 100644 --- a/Cps.Fct.Hk.Ui.Services/WitnessService.cs +++ b/Cps.Fct.Hk.Ui.Services/WitnessService.cs @@ -29,7 +29,7 @@ public class WitnessService( private readonly ICaseLockService caseLockService = caseLockService; /// - public async Task GetWitnessStatementsAsync(int witnessId, CmsAuthValues cmsAuthValues) + public async Task GetWitnessStatementsAsync(int witnessId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string witnessIdString = witnessId.ToString(CultureInfo.InvariantCulture); @@ -39,7 +39,7 @@ public async Task GetWitnessStatementsAsync(int witne var request = new GetWitnessStatementsRequest(witnessId, Guid.NewGuid()); - WitnessStatementsResponse? statements = await this.apiClient.GetWitnessStatementsAsync(request, cmsAuthValues).ConfigureAwait(false); + WitnessStatementsResponse? statements = await this.apiClient.GetWitnessStatementsAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); return statements; } @@ -52,7 +52,7 @@ public async Task GetWitnessStatementsAsync(int witne } /// - public async Task GetCaseWitnessesAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetCaseWitnessesAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { string caseIdString = caseId.ToString(CultureInfo.InvariantCulture); @@ -61,7 +61,7 @@ public async Task GetCaseWitnessesAsync(int caseId, CmsAuthVa this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Getting witnesses for caseId [{caseIdString}]"); var request = new GetCaseWitnessesRequest(caseId, Guid.NewGuid()); - WitnessesResponse? witnesses = await this.apiClient.GetCaseWitnessesAsync(request, cmsAuthValues).ConfigureAwait(false); + WitnessesResponse? witnesses = await this.apiClient.GetCaseWitnessesAsync(request, cmsAuthValues, cancellationToken).ConfigureAwait(false); return witnesses; } @@ -74,13 +74,13 @@ public async Task GetCaseWitnessesAsync(int caseId, CmsAuthVa } /// - public async Task AddWitnessAsync(string urn, int caseId, string firstName, string lastName, CmsAuthValues cmsAuthValues, Guid correspondenceId = default) + public async Task AddWitnessAsync(string urn, int caseId, string firstName, string lastName, CmsAuthValues cmsAuthValues, Guid correspondenceId = default, CancellationToken cancellationToken = default) { try { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Attempting to add witness to case with a caseId [{caseId}]"); - var checkCaeLockStatus = await this.caseLockService.CheckCaseLockAsync(caseId, cmsAuthValues).ConfigureAwait(false); + var checkCaeLockStatus = await this.caseLockService.CheckCaseLockAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); if (checkCaeLockStatus.IsLocked && !checkCaeLockStatus.IsLockedByCurrentUser) { throw new CaseLockedException($"{LoggingConstants.HskUiLogPrefix} Attempting to add case witness for caseId [{caseId}] is failed as case is locked by [{checkCaeLockStatus.LockedByUser}]."); diff --git a/DdeiClient/Clients/BaseCmsClient.cs b/DdeiClient/Clients/BaseCmsClient.cs index c09d13f46..e4aee3f3e 100644 --- a/DdeiClient/Clients/BaseCmsClient.cs +++ b/DdeiClient/Clients/BaseCmsClient.cs @@ -3,6 +3,7 @@ using System.Net; using Common.Extensions; using System.Text.Json; +using System.Threading; namespace DdeiClient.Clients; @@ -16,29 +17,29 @@ protected BaseCmsClient(IJsonConvertWrapper jsonConvertWrapper) } protected abstract HttpClient GetHttpClient(string cmsAuthValues); - protected virtual async Task CallHttpClientAsync(HttpRequestMessage request, string cmsAuthValues) + protected virtual async Task CallHttpClientAsync(HttpRequestMessage request, string cmsAuthValues, CancellationToken cancellationToken = default) { - using var response = await CallHttpClientAsync(request, cmsAuthValues); - var content = await response.Content.ReadAsStringAsync(); + using var response = await CallHttpClientAsync(request, cmsAuthValues, cancellationToken); + var content = await response.Content.ReadAsStringAsync(cancellationToken); - try - { - return this.JsonConvertWrapper.DeserializeObject(content); - } - catch (JsonException ex) - { - // CMS service returned invalid or incompatible JSON - throw new HttpRequestException( - "Invalid JSON received from cms.", - ex, - HttpStatusCode.BadGateway); + try + { + return this.JsonConvertWrapper.DeserializeObject(content); + } + catch (JsonException ex) + { + // CMS service returned invalid or incompatible JSON + throw new HttpRequestException( + "Invalid JSON received from cms.", + ex, + HttpStatusCode.BadGateway); } } - protected virtual async Task CallHttpClientAsync(HttpRequestMessage request, string cmsAuthValues, params HttpStatusCode[] expectedUnhappyStatusCodes) + protected virtual async Task CallHttpClientAsync(HttpRequestMessage request, string cmsAuthValues, CancellationToken cancellationToken = default, params HttpStatusCode[] expectedUnhappyStatusCodes) { var httpClient = GetHttpClient(cmsAuthValues); - var response = await httpClient.SendAsync(request); + var response = await httpClient.SendAsync(request, cancellationToken); try { if (response.IsSuccessStatusCode || expectedUnhappyStatusCodes.Contains(response.StatusCode)) @@ -46,7 +47,7 @@ protected virtual async Task CallHttpClientAsync(HttpReques return response; } - var content = await response.Content.ReadAsStringAsync(); + var content = await response.Content.ReadAsStringAsync(cancellationToken); throw new HttpRequestException(content); } catch (HttpRequestException exception) diff --git a/DdeiClient/Clients/Interfaces/IMasterDataServiceClient.cs b/DdeiClient/Clients/Interfaces/IMasterDataServiceClient.cs index d20b14fed..b35e0dbfd 100644 --- a/DdeiClient/Clients/Interfaces/IMasterDataServiceClient.cs +++ b/DdeiClient/Clients/Interfaces/IMasterDataServiceClient.cs @@ -25,103 +25,115 @@ public interface IMasterDataServiceClient /// /// If the case summary is not found, the method returns null. The caller should handle the null return value accordingly. /// - Task GetCaseSummaryAsync(GetCaseSummaryRequest request, CmsAuthValues cmsAuthValues); + Task GetCaseSummaryAsync(GetCaseSummaryRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Lists the communications for a case identifier. /// /// The request to list communications. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A list of communications for a case identifier. - Task> ListCommunicationsHkAsync(ListCommunicationsHkRequest request, CmsAuthValues cmsAuthValues); + Task> ListCommunicationsHkAsync(ListCommunicationsHkRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Lists the used statements for a case identifier. /// /// The request to list used statements. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A list of used statements for a case identifier. - Task GetUsedStatementsAsync(GetUsedStatementsRequest request, CmsAuthValues cmsAuthValues); + Task GetUsedStatementsAsync(GetUsedStatementsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Lists the used exhibits for a case identifier. /// /// The request to list used exhibits. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A list of used exhibits for a case identifier. - Task GetUsedExhibitsAsync(GetUsedExhibitsRequest request, CmsAuthValues cmsAuthValues); + Task GetUsedExhibitsAsync(GetUsedExhibitsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Lists the used MG forms for a case identifier. /// /// The request to list used MG forms. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A list of used MG forms for a case identifier. - Task GetUsedMgFormsAsync(GetUsedMgFormsRequest request, CmsAuthValues cmsAuthValues); + Task GetUsedMgFormsAsync(GetUsedMgFormsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Lists the used other materials for a case identifier. /// /// The request to list used other materials. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A list of used other materials for a case identifier. - Task GetUsedOtherMaterialsAsync(GetUsedOtherMaterialsRequest request, CmsAuthValues cmsAuthValues); + Task GetUsedOtherMaterialsAsync(GetUsedOtherMaterialsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Lists the unused materials for a case identifier. /// /// The request to list unused materials. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A list of unused materials for a case identifier. - Task GetUnusedMaterialsAsync(GetUnusedMaterialsRequest request, CmsAuthValues cmsAuthValues); + Task GetUnusedMaterialsAsync(GetUnusedMaterialsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Gets exhibit producers for a given case Id. /// /// The request for exhibit producers. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A list of exhibit producers - Task GetExhibitProducersAsync(GetExhibitProducersRequest request, CmsAuthValues cmsAuthValues); + Task GetExhibitProducersAsync(GetExhibitProducersRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Asynchronously retrieves the attachments for a given communication identifier. /// /// The request containing the parameters required to fetch the attachments. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A task representing the asynchronous operation, which returns the attachments response. - Task GetAttachmentsAsync(GetAttachmentsRequest request, CmsAuthValues cmsAuthValues); + Task GetAttachmentsAsync(GetAttachmentsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Asynchronously retrieves the material document for the specified request. /// /// The request containing the document parameters. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A task representing the asynchronous operation, which returns a if the document is found; otherwise, null. - Task GetMaterialDocumentAsync(GetDocumentRequest request, CmsAuthValues cmsAuthValues); + Task GetMaterialDocumentAsync(GetDocumentRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Gets case defendants. /// /// The request to list defendants. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A list of defendants for a case identifier. - Task GetCaseDefendantsAsync(ListCaseDefendantsRequest request, CmsAuthValues cmsAuthValues); + Task GetCaseDefendantsAsync(ListCaseDefendantsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Gets witnesses belonging to the case. /// /// GetWitnessRequest. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// List of witnesses. - Task GetCaseWitnessesAsync(GetCaseWitnessesRequest request, CmsAuthValues cmsAuthValues); + Task GetCaseWitnessesAsync(GetCaseWitnessesRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Gets statements for a witness. /// /// The request object. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// representing the asynchronous operation. - Task GetWitnessStatementsAsync(GetWitnessStatementsRequest request, CmsAuthValues cmsAuthValues); + Task GetWitnessStatementsAsync(GetWitnessStatementsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Adds a case action plan for a given case Id. @@ -129,96 +141,108 @@ public interface IMasterDataServiceClient /// The case Id. /// The request object. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a indicating success without a specific content body. - Task AddCaseActionPlanAsync(int caseId, AddActionPlanRequest request, CmsAuthValues cmsAuthValues); + Task AddCaseActionPlanAsync(int caseId, AddActionPlanRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Asynchronously adds a new case witness. /// /// The request to add a witness. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a indicating success without a specific content body. - Task AddWitnessAsync(AddWitnessRequest request, CmsAuthValues cmsAuthValues); + Task AddWitnessAsync(AddWitnessRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Asynchronously renames a material name using material id. /// /// The request to add a witness. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// RenameMaterialResponse object. - Task RenameMaterialAsync(RenameMaterialRequest request, CmsAuthValues cmsAuthValues); + Task RenameMaterialAsync(RenameMaterialRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Reclassifies a communication for a material identifier. /// /// The request to add a witness. /// CMS authentication values including cookies and tokens. + /// A token to cancel the operation. /// ReclassificationResponse object. - Task ReclassifyCommunicationAsync(ReclassifyCommunicationRequest request, CmsAuthValues cmsAuthValues); + Task ReclassifyCommunicationAsync(ReclassifyCommunicationRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Asynchronously check case lock status. /// /// The ID of the case to be unlocked. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task CheckCaseLockAsync(int caseId, CmsAuthValues cmsAuthValues); + Task CheckCaseLockAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Asynchronously discards a material using material id. /// /// The discard material request. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task DiscardMaterialAsync(DiscardMaterialRequest request, CmsAuthValues cmsAuthValues); + Task DiscardMaterialAsync(DiscardMaterialRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Asynchronously discards a material using material id. /// /// The discard material request. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task SetMaterialReadStatusAsync(SetMaterialReadStatusRequest request, CmsAuthValues cmsAuthValues); + Task SetMaterialReadStatusAsync(SetMaterialReadStatusRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get PCD Requests core/basic info by case id. /// /// The GetPcdRequestsCore request. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task> GetPcdRequestCoreAsync(GetPcdRequestsCoreRequest request, CmsAuthValues cmsAuthValues); + Task> GetPcdRequestCoreAsync(GetPcdRequestsCoreRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get PCD request overview by case id and PCD id. /// /// The GetPcdRequestByPcdIdCore request. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetPcdRequestByPcdIdAsync(GetPcdRequestByPcdIdCoreRequest request, CmsAuthValues cmsAuthValues); + Task GetPcdRequestByPcdIdAsync(GetPcdRequestByPcdIdCoreRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Asynchronously updates statemenet. /// /// The statement request. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task UpdateStatementAsync(UpdateStatementRequest request, CmsAuthValues cmsAuthValues); + Task UpdateStatementAsync(UpdateStatementRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Asynchronously updates exhibit. /// /// The exhibit request. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task UpdateExhibitAsync(UpdateExhibitRequest request, CmsAuthValues cmsAuthValues); + Task UpdateExhibitAsync(UpdateExhibitRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get First Initial Review Get Case History. /// /// The caseId. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task FirstInitialReviewGetCaseHistoryAsync(int caseId, CmsAuthValues cmsAuthValues); + Task FirstInitialReviewGetCaseHistoryAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get Initial Review ByHistoryId. @@ -228,7 +252,7 @@ public interface IMasterDataServiceClient /// The CMS authentication values required for the API call. /// The cancellationToken required for the API call. /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetInitialReviewByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken); + Task GetInitialReviewByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get PCD Analysis ByHistoryId. @@ -238,15 +262,16 @@ public interface IMasterDataServiceClient /// The CMS authentication values required for the API call. /// The cancellationToken required for the API call. /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetPcdAnalysisByIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken); + Task GetPcdAnalysisByIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// GetHistoryEvents. /// /// The caseId. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task> GetHistoryEventsAsync(int caseId, CmsAuthValues cmsAuthValues); + Task> GetHistoryEventsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get Offence Charge. @@ -254,16 +279,18 @@ public interface IMasterDataServiceClient /// The caseId. /// History Id. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetOffenceChargeByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues); + Task GetOffenceChargeByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get Pre Charge Decision Case History Event Details. /// /// Case Id. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetPreChargeDecisionCaseHistoryEventDetailsAsync(int caseId, CmsAuthValues cmsAuthValues); + Task GetPreChargeDecisionCaseHistoryEventDetailsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get Pre Charge Decision By History Id. @@ -271,15 +298,17 @@ public interface IMasterDataServiceClient /// Case Id. /// History Id. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetPreChargeDecisionByHistoryId(int caseId, int historyId, CmsAuthValues cmsAuthValues); + Task GetPreChargeDecisionByHistoryId(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); /// /// Get Pcd review. /// /// The case id. /// The CMS authentication values required for the API call. + /// A token to cancel the operation. /// A representing the asynchronous operation, with a string containing the lock release status. - Task GetPcdReview(int caseId, CmsAuthValues cmsAuthValues); + Task GetPcdReview(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/DdeiClient/Clients/Interfaces/IMdsClient.cs b/DdeiClient/Clients/Interfaces/IMdsClient.cs index 5a88e38d8..94a2cbc8f 100644 --- a/DdeiClient/Clients/Interfaces/IMdsClient.cs +++ b/DdeiClient/Clients/Interfaces/IMdsClient.cs @@ -7,52 +7,53 @@ using DdeiClient.Domain.Args; using Ddei.Domain.Response.Document; using Ddei.Domain.Response; +using System.Threading; namespace DdeiClient.Clients.Interfaces; public interface IMdsClient { - Task GetUrnFromCaseIdAsync(MdsCaseIdOnlyArgDto arg); + Task GetUrnFromCaseIdAsync(MdsCaseIdOnlyArgDto arg, CancellationToken cancellationToken = default); - Task GetCaseSummaryAsync(MdsCaseIdOnlyArgDto arg); + Task GetCaseSummaryAsync(MdsCaseIdOnlyArgDto arg, CancellationToken cancellationToken = default); - Task> GetPcdRequestsCoreAsync(MdsCaseIdentifiersArgDto arg); + Task> GetPcdRequestsCoreAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default); - Task> GetPcdRequestsAsync(MdsCaseIdentifiersArgDto arg); + Task> GetPcdRequestsAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default); - Task GetPcdRequestAsync(MdsPcdArgDto arg); + Task GetPcdRequestAsync(MdsPcdArgDto arg, CancellationToken cancellationToken = default); - Task GetDefendantAndChargesAsync(MdsCaseIdentifiersArgDto arg); + Task GetDefendantAndChargesAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default); - Task> ListDocumentsAsync(MdsCaseIdentifiersArgDto arg); + Task> ListDocumentsAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default); - Task GetDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg); + Task GetDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg, CancellationToken cancellationToken = default); - Task CheckoutDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg); + Task CheckoutDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg, CancellationToken cancellationToken = default); - Task CancelCheckoutDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg); + Task CancelCheckoutDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg, CancellationToken cancellationToken = default); - Task UploadPdfAsync(MdsMaterialIdAndDocumentIdArgDto arg, Stream stream); + Task UploadPdfAsync(MdsMaterialIdAndDocumentIdArgDto arg, Stream stream, CancellationToken cancellationToken = default); - Task> GetDocumentNotesAsync(MdsDocumentArgDto arg); + Task> GetDocumentNotesAsync(MdsDocumentArgDto arg, CancellationToken cancellationToken = default); - Task AddDocumentNoteAsync(MdsAddDocumentNoteArgDto arg); + Task AddDocumentNoteAsync(MdsAddDocumentNoteArgDto arg, CancellationToken cancellationToken = default); - Task RenameDocumentAsync(MdsRenameDocumentArgDto arg); + Task RenameDocumentAsync(MdsRenameDocumentArgDto arg, CancellationToken cancellationToken = default); - Task RenameExhibitAsync(MdsRenameDocumentArgDto arg); + Task RenameExhibitAsync(MdsRenameDocumentArgDto arg, CancellationToken cancellationToken = default); - Task ReclassifyCommunicationAsync(MdsReclassifyCommunicationArgDto arg); + Task ReclassifyCommunicationAsync(MdsReclassifyCommunicationArgDto arg, CancellationToken cancellationToken = default); - Task> GetExhibitProducersAsync(MdsCaseIdentifiersArgDto arg); + Task> GetExhibitProducersAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default); - Task> GetWitnessesAsync(MdsCaseIdentifiersArgDto arg); + Task> GetWitnessesAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default); - Task> GetMaterialTypeListAsync(CmsBaseArgDto arg); + Task> GetMaterialTypeListAsync(CmsBaseArgDto arg, CancellationToken cancellationToken = default); - Task> GetWitnessStatementsAsync(MdsWitnessStatementsArgDto arg); + Task> GetWitnessStatementsAsync(MdsWitnessStatementsArgDto arg, CancellationToken cancellationToken = default); - Task ToggleIsUnusedDocumentAsync(MdsToggleIsUnusedDocumentDto toggleIsUnusedDocumentDto); + Task ToggleIsUnusedDocumentAsync(MdsToggleIsUnusedDocumentDto toggleIsUnusedDocumentDto, CancellationToken cancellationToken = default); - Task> ListCaseIdsAsync(MdsUrnArgDto arg); + Task> ListCaseIdsAsync(MdsUrnArgDto arg, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/DdeiClient/Clients/MasterDataServiceClient.cs b/DdeiClient/Clients/MasterDataServiceClient.cs index f2884994a..94e9d58c2 100644 --- a/DdeiClient/Clients/MasterDataServiceClient.cs +++ b/DdeiClient/Clients/MasterDataServiceClient.cs @@ -32,7 +32,7 @@ public class MasterDataServiceClient(IMasterDataServiceApiClientFactory mdsApiCl private readonly ILogger logger = loggerFactory.CreateLogger(); /// - public async Task GetCaseSummaryAsync(GetCaseSummaryRequest request, CmsAuthValues cmsAuthValues) + public async Task GetCaseSummaryAsync(GetCaseSummaryRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -47,7 +47,7 @@ public async Task GetCaseSummaryAsync(GetCaseSummaryRequest { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetCaseSummaryAsync(request.CaseId); + var data = await client.GetCaseSummaryAsync(request.CaseId, cancellationToken); if (data?.Urn is not null) { @@ -72,7 +72,7 @@ public async Task GetCaseSummaryAsync(GetCaseSummaryRequest } /// - public async Task GetUnusedMaterialsAsync(GetUnusedMaterialsRequest request, CmsAuthValues cmsAuthValues) + public async Task GetUnusedMaterialsAsync(GetUnusedMaterialsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -83,11 +83,11 @@ public async Task GetUnusedMaterialsAsync(GetUnusedMate try { - UnusedMaterialsResponse results = new (); + UnusedMaterialsResponse results = new(); var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); string additionalInfo = $"received #0 unused materials"; - var data = await client.GetUnusedMaterialsAsync(request.CaseId); + var data = await client.GetUnusedMaterialsAsync(request.CaseId, cancellationToken); if (data is not null) { @@ -153,7 +153,7 @@ public async Task GetUnusedMaterialsAsync(GetUnusedMate } /// - public async Task GetUsedExhibitsAsync(GetUsedExhibitsRequest request, CmsAuthValues cmsAuthValues) + public async Task GetUsedExhibitsAsync(GetUsedExhibitsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -163,12 +163,12 @@ public async Task GetUsedExhibitsAsync(GetUsedExhibitsRequ try { - UsedExhibitsResponse results = new () { Exhibits = new List() }; + UsedExhibitsResponse results = new() { Exhibits = new List() }; var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); string additionalInfo = $"received #0 used exhibits"; - var data = await client.GetUsedExhibitsAsync(request.CaseId); + var data = await client.GetUsedExhibitsAsync(request.CaseId, cancellationToken); if (data?.Exhibits is not null) { @@ -204,7 +204,7 @@ public async Task GetUsedExhibitsAsync(GetUsedExhibitsRequ } /// - public async Task GetUsedMgFormsAsync(GetUsedMgFormsRequest request, CmsAuthValues cmsAuthValues) + public async Task GetUsedMgFormsAsync(GetUsedMgFormsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -214,10 +214,10 @@ public async Task GetUsedMgFormsAsync(GetUsedMgFormsRequest try { - UsedMgFormsResponse results = new () { MgForms = new List() }; + UsedMgFormsResponse results = new() { MgForms = new List() }; var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetUsedMgFormsAsync(request.CaseId); + var data = await client.GetUsedMgFormsAsync(request.CaseId, cancellationToken); if (data?.MgForms is not null) { @@ -250,7 +250,7 @@ public async Task GetUsedMgFormsAsync(GetUsedMgFormsRequest } /// - public async Task GetUsedOtherMaterialsAsync(GetUsedOtherMaterialsRequest request, CmsAuthValues cmsAuthValues) + public async Task GetUsedOtherMaterialsAsync(GetUsedOtherMaterialsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -260,10 +260,10 @@ public async Task GetUsedOtherMaterialsAsync(GetUsed try { - UsedOtherMaterialsResponse results = new () { MgForms = new List() }; + UsedOtherMaterialsResponse results = new() { MgForms = new List() }; var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetUsedOtherMaterialsAsync(request.CaseId); + var data = await client.GetUsedOtherMaterialsAsync(request.CaseId, cancellationToken); if (data?.MgForms is not null) { @@ -295,7 +295,7 @@ public async Task GetUsedOtherMaterialsAsync(GetUsed } /// - public async Task GetUsedStatementsAsync(GetUsedStatementsRequest request, CmsAuthValues cmsAuthValues) + public async Task GetUsedStatementsAsync(GetUsedStatementsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -305,12 +305,12 @@ public async Task GetUsedStatementsAsync(GetUsedStatemen try { - UsedStatementsResponse results = new () { Statements = new List() }; + UsedStatementsResponse results = new() { Statements = new List() }; var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); string additionalInfo = $"received #0 used statements"; - var data = await client.GetUsedStatementsAsync(request.CaseId); + var data = await client.GetUsedStatementsAsync(request.CaseId, cancellationToken); if (data?.Statements is not null) { @@ -346,7 +346,7 @@ public async Task GetUsedStatementsAsync(GetUsedStatemen } /// - public async Task> ListCommunicationsHkAsync(ListCommunicationsHkRequest request, CmsAuthValues cmsAuthValues) + public async Task> ListCommunicationsHkAsync(ListCommunicationsHkRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -356,11 +356,11 @@ public async Task> ListCommunicationsHkAsync( try { - List results = new (); + List results = new(); var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); string additionalInfo = $"received #0 communications"; - var data = await client.ListCommunicationsHkAsync(request.CaseId); + var data = await client.ListCommunicationsHkAsync(request.CaseId, cancellationToken); if (data is not null) { @@ -399,21 +399,21 @@ public async Task> ListCommunicationsHkAsync( } /// - public async Task GetAttachmentsAsync(GetAttachmentsRequest request, CmsAuthValues cmsAuthValues) + public async Task GetAttachmentsAsync(GetAttachmentsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); var stopwatch = Stopwatch.StartNew(); const string OperationName = "GetAttachments"; - AttachmentsResponse results = new (); + AttachmentsResponse results = new(); try { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); string additionalInfo = $"received #0 attachments"; - var data = await client.GetAttachmentsAsync(request.CommunicationId); + var data = await client.GetAttachmentsAsync(request.CommunicationId, cancellationToken); if (data?.Attachments is not null) { results = new AttachmentsResponse @@ -454,7 +454,7 @@ public async Task GetAttachmentsAsync(GetAttachmentsRequest } /// - public async Task GetMaterialDocumentAsync(GetDocumentRequest request, CmsAuthValues cmsAuthValues) + public async Task GetMaterialDocumentAsync(GetDocumentRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -469,7 +469,7 @@ public async Task GetAttachmentsAsync(GetAttachmentsRequest int caseId = int.Parse(request.CaseId); - var data = await client.GetMaterialDocumentAsync(caseId, request.FilePath); + var data = await client.GetMaterialDocumentAsync(caseId, request.FilePath, cancellationToken); string fileDownloadName = Path.GetFileName(request.FilePath); string? contentType = FileUtils.GetMimeType(fileDownloadName) ?? @@ -493,14 +493,14 @@ public async Task GetAttachmentsAsync(GetAttachmentsRequest } /// - public async Task GetExhibitProducersAsync(GetExhibitProducersRequest request, CmsAuthValues cmsAuthValues) + public async Task GetExhibitProducersAsync(GetExhibitProducersRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); var stopwatch = Stopwatch.StartNew(); const string OperationName = "GetExhibitProducers"; - ExhibitProducersResponse results = new (); + ExhibitProducersResponse results = new(); try { @@ -508,7 +508,7 @@ public async Task GetExhibitProducersAsync(GetExhibitP string additionalInfo = "received #0 producers"; - var data = await client.GetExhibitProducersAsync(request.CaseId); + var data = await client.GetExhibitProducersAsync(request.CaseId, cancellationToken); if (data != null) { @@ -538,20 +538,20 @@ public async Task GetExhibitProducersAsync(GetExhibitP } /// - public async Task GetCaseDefendantsAsync(ListCaseDefendantsRequest request, CmsAuthValues cmsAuthValues) + public async Task GetCaseDefendantsAsync(ListCaseDefendantsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); var stopwatch = Stopwatch.StartNew(); const string OperationName = "ListCaseDefendants"; - List results = new (); + List results = new(); try { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.ListCaseDefendantsAsync(request.CaseId); + var data = await client.ListCaseDefendantsAsync(request.CaseId, cancellationToken); var listCaseDefendantsResponse = new DefendantsResponse { @@ -590,7 +590,7 @@ public async Task GetCaseDefendantsAsync(ListCaseDefendantsR } /// - public async Task GetCaseWitnessesAsync(GetCaseWitnessesRequest request, CmsAuthValues cmsAuthValues) + public async Task GetCaseWitnessesAsync(GetCaseWitnessesRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -598,13 +598,13 @@ public async Task GetCaseWitnessesAsync(GetCaseWitnessesReque var stopwatch = Stopwatch.StartNew(); const string OperationName = "ListCaseWitnesses"; string additionalInfo = $"received #0 witnesses"; - WitnessesResponse results = new (); + WitnessesResponse results = new(); try { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.ListCaseWitnessesAsync(request.CaseId); + var data = await client.ListCaseWitnessesAsync(request.CaseId, null, null, cancellationToken); if (data is not null) { @@ -632,7 +632,7 @@ public async Task GetCaseWitnessesAsync(GetCaseWitnessesReque } /// - public async Task GetWitnessStatementsAsync(GetWitnessStatementsRequest request, CmsAuthValues cmsAuthValues) + public async Task GetWitnessStatementsAsync(GetWitnessStatementsRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -641,13 +641,13 @@ public async Task GetWitnessStatementsAsync(GetWitnes const string OperationName = "GetStatementsForWitness"; string additionalInfo = "received #0 statements"; - WitnessStatementsResponse results = new (); + WitnessStatementsResponse results = new(); try { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetStatementsForWitnessAsync(request.WitnessId); + var data = await client.GetStatementsForWitnessAsync(request.WitnessId, cancellationToken); if (data?.StatementsForWitness is not null) { @@ -670,7 +670,7 @@ public async Task GetWitnessStatementsAsync(GetWitnes } /// - public async Task AddCaseActionPlanAsync(int caseId, AddActionPlanRequest request, CmsAuthValues cmsAuthValues) + public async Task AddCaseActionPlanAsync(int caseId, AddActionPlanRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -684,7 +684,7 @@ public async Task AddCaseActionPlanAsync(int caseId, AddActionP var mdsRequest = MapApAction(request); - await client.AddActionPlanAsync(caseId, mdsRequest); + await client.AddActionPlanAsync(caseId, mdsRequest, cancellationToken); this.LogOperationCompletedEvent(OperationName, request, stopwatch.Elapsed, string.Empty); return new NoContentResult(); @@ -697,7 +697,7 @@ public async Task AddCaseActionPlanAsync(int caseId, AddActionP } /// - public async Task AddWitnessAsync(AddWitnessRequest request, CmsAuthValues cmsAuthValues) + public async Task AddWitnessAsync(AddWitnessRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(request.caseId); @@ -718,7 +718,7 @@ public async Task AddWitnessAsync(AddWitnessRequest request, Cm FirstName = request.FirstName, }; - await client.AddWitnessAsync(request.caseId, mdsRequest); + await client.AddWitnessAsync(request.caseId, mdsRequest, cancellationToken); this.LogOperationCompletedEvent(OperationName, request, stopwatch.Elapsed, string.Empty); return new NoContentResult(); } @@ -730,7 +730,7 @@ public async Task AddWitnessAsync(AddWitnessRequest request, Cm } /// - public async Task RenameMaterialAsync(RenameMaterialRequest request, CmsAuthValues cmsAuthValues) + public async Task RenameMaterialAsync(RenameMaterialRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(request.materialId); @@ -749,14 +749,14 @@ public async Task RenameMaterialAsync(RenameMaterialRequ Subject = request.subject, }; - var data = await client.RenameMaterialAsync(request.materialId, mdsRequest); + var data = await client.RenameMaterialAsync(request.materialId, mdsRequest, cancellationToken); if (data?.UpdateCommunication?.Id == null) { return default; } - RenameMaterialResponse result = new (new RenameMaterialData { Id = data.UpdateCommunication.Id }); + RenameMaterialResponse result = new(new RenameMaterialData { Id = data.UpdateCommunication.Id }); this.LogOperationCompletedEvent(OperationName, request, stopwatch.Elapsed, string.Empty); @@ -770,7 +770,7 @@ public async Task RenameMaterialAsync(RenameMaterialRequ } /// - public async Task ReclassifyCommunicationAsync(ReclassifyCommunicationRequest request, CmsAuthValues cmsAuthValues) + public async Task ReclassifyCommunicationAsync(ReclassifyCommunicationRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -793,7 +793,7 @@ public async Task ReclassifyCommunicationAsync(Reclass Exhibit = MapExhibit(request.Exhibit), }; - var data = await client.ReclassifyCommunicationAsync(mdsRequest); + var data = await client.ReclassifyCommunicationAsync(mdsRequest, cancellationToken); if (data?.ReclassifyCommunication?.Id == null) { @@ -813,7 +813,7 @@ public async Task ReclassifyCommunicationAsync(Reclass } /// - public async Task CheckCaseLockAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task CheckCaseLockAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { if (caseId <= 0) { @@ -831,7 +831,7 @@ public async Task CheckCaseLockAsync(int caseId, CmsAuth { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetCaseLockStatusAsync(caseId); + var data = await client.GetCaseLockStatusAsync(caseId, cancellationToken); var result = new CaseLockedStatusResult { @@ -851,7 +851,7 @@ public async Task CheckCaseLockAsync(int caseId, CmsAuth } /// - public async Task DiscardMaterialAsync(DiscardMaterialRequest request, CmsAuthValues cmsAuthValues) + public async Task DiscardMaterialAsync(DiscardMaterialRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(request.materialId); @@ -871,7 +871,7 @@ public async Task DiscardMaterialAsync(DiscardMaterialR DiscardReasonDescription = request.discardReasonDescription, }; - var data = await client.DiscardMaterialAsync(request.materialId, mdsReqest); + var data = await client.DiscardMaterialAsync(request.materialId, mdsReqest, cancellationToken); this.LogOperationCompletedEvent(OperationName, request, stopwatch.Elapsed, string.Empty); if (data?.DiscardMaterial?.Id == null) @@ -890,7 +890,7 @@ public async Task DiscardMaterialAsync(DiscardMaterialR } /// - public async Task SetMaterialReadStatusAsync(SetMaterialReadStatusRequest request, CmsAuthValues cmsAuthValues) + public async Task SetMaterialReadStatusAsync(SetMaterialReadStatusRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(request.materialId); @@ -908,7 +908,7 @@ public async Task SetMaterialReadStatusAsync(SetM State = (ApiClient.MaterialReadStateRequestState)request.state, }; - var data = await client.SetMaterialReadStateAsync(request.materialId, mdsReqest); + var data = await client.SetMaterialReadStateAsync(request.materialId, mdsReqest, cancellationToken); if (data?.CompleteCommunication?.Id == null) { @@ -929,7 +929,7 @@ public async Task SetMaterialReadStatusAsync(SetM } /// - public async Task> GetPcdRequestCoreAsync(GetPcdRequestsCoreRequest request, CmsAuthValues cmsAuthValues) + public async Task> GetPcdRequestCoreAsync(GetPcdRequestsCoreRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -937,12 +937,12 @@ public async Task> GetPcdRequestCoreAsync(GetPcdRequestsCor var stopwatch = Stopwatch.StartNew(); const string OperationName = "PcdRequestCore"; - List results = new (); + List results = new(); try { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetCasePcdRequestCoreAsync(request.caseId); + var data = await client.GetCasePcdRequestCoreAsync(request.caseId, cancellationToken); if (data is not null) { @@ -967,7 +967,7 @@ public async Task> GetPcdRequestCoreAsync(GetPcdRequestsCor } /// - public async Task GetPcdRequestByPcdIdAsync(GetPcdRequestByPcdIdCoreRequest request, CmsAuthValues cmsAuthValues) + public async Task GetPcdRequestByPcdIdAsync(GetPcdRequestByPcdIdCoreRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -981,7 +981,7 @@ public async Task GetPcdRequestByPcdIdAsync(GetPcdRequestByPcdIdC { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetCasePcdRequestByPcdIdAsync(request.caseId, request.pcdId); + var data = await client.GetCasePcdRequestByPcdIdAsync(request.caseId, request.pcdId, cancellationToken); if (data is not null) { @@ -1039,7 +1039,7 @@ public async Task GetPcdRequestByPcdIdAsync(GetPcdRequestByPcdIdC } /// - public async Task UpdateStatementAsync(UpdateStatementRequest request, CmsAuthValues cmsAuthValues) + public async Task UpdateStatementAsync(UpdateStatementRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(request.MaterialId); @@ -1065,7 +1065,7 @@ public async Task UpdateStatementAsync(UpdateStatementR Used = request.Used, }; - var data = await client.UpdateStatementAsync(request.CaseId, request.MaterialId, mdsRequest); + var data = await client.UpdateStatementAsync(request.CaseId, request.MaterialId, mdsRequest, cancellationToken); if (data?.UpdateStatement?.Id == null) { @@ -1084,7 +1084,7 @@ public async Task UpdateStatementAsync(UpdateStatementR } /// - public async Task UpdateExhibitAsync(UpdateExhibitRequest request, CmsAuthValues cmsAuthValues) + public async Task UpdateExhibitAsync(UpdateExhibitRequest request, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(request); Requires.NotNull(request.CaseId); @@ -1115,7 +1115,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest Used = request.Used, }; - var data = await client.UpdateExhibitAsync(request.CaseId, request.MaterialId, mdsRequest); + var data = await client.UpdateExhibitAsync(request.CaseId, request.MaterialId, mdsRequest, cancellationToken); if (data?.UpdateExhibit?.Id == null) { @@ -1135,7 +1135,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest } /// - public async Task FirstInitialReviewGetCaseHistoryAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task FirstInitialReviewGetCaseHistoryAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(caseId); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -1147,7 +1147,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.FirstInitialReviewGetCaseHistoryAsync(caseId); + var data = await client.FirstInitialReviewGetCaseHistoryAsync(caseId, cancellationToken); return data; } catch (Exception exception) @@ -1199,7 +1199,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetPcdAnalysisByIdAsync(caseId, historyId); + var data = await client.GetPcdAnalysisByIdAsync(caseId, historyId, cancellationToken); return data; } catch (Exception exception) @@ -1210,7 +1210,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest } /// - public async Task> GetHistoryEventsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task> GetHistoryEventsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(caseId); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -1222,7 +1222,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetCaseHistoryEventsAsync(caseId); + var data = await client.GetCaseHistoryEventsAsync(caseId, null, cancellationToken); return data; } catch (Exception exception) @@ -1233,7 +1233,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest } /// - public async Task GetOffenceChargeByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues) + public async Task GetOffenceChargeByHistoryIdAsync(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(caseId); Requires.NotNull(historyId); @@ -1246,7 +1246,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetOffenceChangeByIdAsync(caseId, historyId); + var data = await client.GetOffenceChangeByIdAsync(caseId, historyId, cancellationToken); return data; } catch (Exception exception) @@ -1257,7 +1257,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest } /// - public async Task GetPreChargeDecisionCaseHistoryEventDetailsAsync(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetPreChargeDecisionCaseHistoryEventDetailsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(caseId); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -1269,7 +1269,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetPreChargeDecisionOutcomesAsync(caseId); + var data = await client.GetPreChargeDecisionOutcomesAsync(caseId, cancellationToken); return data; } catch (Exception exception) @@ -1280,7 +1280,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest } /// - public async Task GetPreChargeDecisionByHistoryId(int caseId, int historyId, CmsAuthValues cmsAuthValues) + public async Task GetPreChargeDecisionByHistoryId(int caseId, int historyId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(caseId); Requires.NotNull(historyId); @@ -1293,7 +1293,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetPreChargeDecisionByIdAsync(caseId, historyId); + var data = await client.GetPreChargeDecisionByIdAsync(caseId, historyId, cancellationToken); return data; } catch (Exception exception) @@ -1304,7 +1304,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest } /// - public async Task GetPcdReview(int caseId, CmsAuthValues cmsAuthValues) + public async Task GetPcdReview(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { Requires.NotNull(caseId); Requires.NotNull(cmsAuthValues.CmsAuthFullValue); @@ -1316,7 +1316,7 @@ public async Task UpdateExhibitAsync(UpdateExhibitRequest { var client = this.mdsApiClientFactory.Create(cmsAuthValues.CmsAuthFullValue); - var data = await client.GetPcdReviewAsync(caseId); + var data = await client.GetPcdReviewAsync(caseId, cancellationToken); return data; } catch (Exception exception) diff --git a/DdeiClient/Clients/MdsClient.cs b/DdeiClient/Clients/MdsClient.cs index 7cc55892d..28ac524dd 100644 --- a/DdeiClient/Clients/MdsClient.cs +++ b/DdeiClient/Clients/MdsClient.cs @@ -19,6 +19,7 @@ using DdeiClient.Factories; using Microsoft.Extensions.Logging; using System.Net; +using System.Threading; namespace DdeiClient.Clients; @@ -68,158 +69,158 @@ public MdsClient( _jsonConvertWrapper = jsonConvertWrapper.ExceptionIfNull(); } - public async Task GetUrnFromCaseIdAsync(MdsCaseIdOnlyArgDto arg) + public async Task GetUrnFromCaseIdAsync(MdsCaseIdOnlyArgDto arg, CancellationToken cancellationToken = default) { - var mdsCaseIdentifiersDto = await CallHttpClientAsync(_mdsClientRequestFactory.CreateUrnLookupRequest(arg), arg.CmsAuthValues); + var mdsCaseIdentifiersDto = await CallHttpClientAsync(_mdsClientRequestFactory.CreateUrnLookupRequest(arg), arg.CmsAuthValues, cancellationToken); return _caseIdentifiersMapper.MapCaseIdentifiers(mdsCaseIdentifiersDto); } - public async Task GetCaseSummaryAsync(MdsCaseIdOnlyArgDto arg) + public async Task GetCaseSummaryAsync(MdsCaseIdOnlyArgDto arg, CancellationToken cancellationToken = default) { - var mdsResult = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetCaseSummary(arg), arg.CmsAuthValues); + var mdsResult = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetCaseSummary(arg), arg.CmsAuthValues, cancellationToken); return _caseDetailsMapper.Map(mdsResult); } - public async Task> GetPcdRequestsCoreAsync(MdsCaseIdentifiersArgDto arg) + public async Task> GetPcdRequestsCoreAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default) { - var pcdRequests = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateGetPcdRequestsRequest(arg), arg.CmsAuthValues); + var pcdRequests = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateGetPcdRequestsRequest(arg), arg.CmsAuthValues, cancellationToken); return _caseDetailsMapper.MapCorePreChargeDecisionRequests(pcdRequests); } - public async Task> GetPcdRequestsAsync(MdsCaseIdentifiersArgDto arg) + public async Task> GetPcdRequestsAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default) { - var pcdRequests = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateGetPcdRequestsRequest(arg), arg.CmsAuthValues); + var pcdRequests = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateGetPcdRequestsRequest(arg), arg.CmsAuthValues, cancellationToken); return _caseDetailsMapper.MapPreChargeDecisionRequests(pcdRequests); } - public async Task GetPcdRequestAsync(MdsPcdArgDto arg) + public async Task GetPcdRequestAsync(MdsPcdArgDto arg, CancellationToken cancellationToken = default) { - var pcdRequest = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetPcdRequest(arg), arg.CmsAuthValues); + var pcdRequest = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetPcdRequest(arg), arg.CmsAuthValues, cancellationToken); return _caseDetailsMapper.MapPreChargeDecisionRequest(pcdRequest); } - public async Task GetDefendantAndChargesAsync(MdsCaseIdentifiersArgDto arg) + public async Task GetDefendantAndChargesAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default) { - var response = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetDefendantAndChargesRequest(arg), arg.CmsAuthValues); - var content = await response.Content.ReadAsStringAsync(); + var response = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetDefendantAndChargesRequest(arg), arg.CmsAuthValues, cancellationToken); + var content = await response.Content.ReadAsStringAsync(cancellationToken); var defendantAndCharges = _jsonConvertWrapper.DeserializeObject>(content); var etag = response.Headers.ETag?.Tag; return _caseDetailsMapper.MapDefendantsAndCharges(defendantAndCharges, arg.CaseId, etag); } - public async Task> ListDocumentsAsync(MdsCaseIdentifiersArgDto arg) + public async Task> ListDocumentsAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default) { - var mdsResults = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateListCaseDocumentsRequest(arg), arg.CmsAuthValues); + var mdsResults = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateListCaseDocumentsRequest(arg), arg.CmsAuthValues, cancellationToken); return mdsResults.Select(mdsResult => _caseDocumentMapper.Map(mdsResult)); } - public async Task GetDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg) + public async Task GetDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg, CancellationToken cancellationToken = default) { - var response = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetDocumentRequest(arg), arg.CmsAuthValues); + var response = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetDocumentRequest(arg), arg.CmsAuthValues, cancellationToken); var fileName = response.Content.Headers.GetValues("Content-Disposition").ToList()[0]; return new FileResult { - Stream = await response.Content.ReadAsStreamAsync(), + Stream = await response.Content.ReadAsStreamAsync(cancellationToken), FileName = fileName, }; } - public async Task CheckoutDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg) + public async Task CheckoutDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg, CancellationToken cancellationToken = default) { await CallHttpClientAsync( - _mdsClientRequestFactory.CreateCheckoutDocumentRequest(arg), arg.CmsAuthValues); + _mdsClientRequestFactory.CreateCheckoutDocumentRequest(arg), arg.CmsAuthValues, cancellationToken); return new CheckoutDocumentDto { IsSuccess = true }; } - public async Task CancelCheckoutDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg) + public async Task CancelCheckoutDocumentAsync(MdsMaterialIdAndDocumentIdArgDto arg, CancellationToken cancellationToken = default) { - await CallHttpClientAsync(_mdsClientRequestFactory.CreateCancelCheckoutDocumentRequest(arg), arg.CmsAuthValues); + await CallHttpClientAsync(_mdsClientRequestFactory.CreateCancelCheckoutDocumentRequest(arg), arg.CmsAuthValues, cancellationToken); } - public async Task UploadPdfAsync(MdsMaterialIdAndDocumentIdArgDto arg, Stream stream) + public async Task UploadPdfAsync(MdsMaterialIdAndDocumentIdArgDto arg, Stream stream, CancellationToken cancellationToken = default) { - return await CallHttpClientAsync(_mdsClientRequestFactory.CreateUploadPdfRequest(arg, stream), arg.CmsAuthValues, + return await CallHttpClientAsync(_mdsClientRequestFactory.CreateUploadPdfRequest(arg, stream), arg.CmsAuthValues, cancellationToken, [ HttpStatusCode.Gone, HttpStatusCode.RequestEntityTooLarge ]); } - public async Task> GetDocumentNotesAsync(MdsDocumentArgDto arg) + public async Task> GetDocumentNotesAsync(MdsDocumentArgDto arg, CancellationToken cancellationToken = default) { - var mdsResults = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateGetDocumentNotesRequest(arg), arg.CmsAuthValues); + var mdsResults = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateGetDocumentNotesRequest(arg), arg.CmsAuthValues, cancellationToken); return mdsResults.Select(mdsResult => _caseDocumentNoteMapper.Map(mdsResult)).ToArray(); } - public async Task AddDocumentNoteAsync(MdsAddDocumentNoteArgDto arg) + public async Task AddDocumentNoteAsync(MdsAddDocumentNoteArgDto arg, CancellationToken cancellationToken = default) { - var response = await CallHttpClientAsync(_mdsClientRequestFactory.CreateAddDocumentNoteRequest(arg), arg.CmsAuthValues); + var response = await CallHttpClientAsync(_mdsClientRequestFactory.CreateAddDocumentNoteRequest(arg), arg.CmsAuthValues, cancellationToken); return _caseDocumentNoteResultMapper.Map(response); } - public async Task RenameDocumentAsync(MdsRenameDocumentArgDto arg) + public async Task RenameDocumentAsync(MdsRenameDocumentArgDto arg, CancellationToken cancellationToken = default) { - var response = await CallHttpClientAsync(_mdsClientRequestFactory.CreateRenameDocumentRequest(arg), arg.CmsAuthValues); + var response = await CallHttpClientAsync(_mdsClientRequestFactory.CreateRenameDocumentRequest(arg), arg.CmsAuthValues, cancellationToken); return new DocumentRenamedResultDto { Id = response.UpdateCommunication.Id }; } - public async Task RenameExhibitAsync(MdsRenameDocumentArgDto arg) + public async Task RenameExhibitAsync(MdsRenameDocumentArgDto arg, CancellationToken cancellationToken = default) { - var response = await CallHttpClientAsync(_mdsClientRequestFactory.CreateRenameExhibitRequest(arg), arg.CmsAuthValues); + var response = await CallHttpClientAsync(_mdsClientRequestFactory.CreateRenameExhibitRequest(arg), arg.CmsAuthValues, cancellationToken); return new DocumentRenamedResultDto { Id = response.UpdateCommunicationDescription.Id }; } - public async Task ReclassifyCommunicationAsync(MdsReclassifyCommunicationArgDto arg) + public async Task ReclassifyCommunicationAsync(MdsReclassifyCommunicationArgDto arg, CancellationToken cancellationToken = default) { - return await CallHttpClientAsync(_mdsClientRequestFactory.CreateReclassifyCommunicationRequest(arg), arg.CmsAuthValues); + return await CallHttpClientAsync(_mdsClientRequestFactory.CreateReclassifyCommunicationRequest(arg), arg.CmsAuthValues, cancellationToken); } - public async Task> GetExhibitProducersAsync(MdsCaseIdentifiersArgDto arg) + public async Task> GetExhibitProducersAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default) { - var mdsResults = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetExhibitProducersRequest(arg), arg.CmsAuthValues); + var mdsResults = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetExhibitProducersRequest(arg), arg.CmsAuthValues, cancellationToken); return mdsResults.ExhibitProducers.Select(_caseExhibitProducerMapper.Map); } - public async Task> GetWitnessesAsync(MdsCaseIdentifiersArgDto arg) + public async Task> GetWitnessesAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default) { - var mdsResults = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateCaseWitnessesRequest(arg), arg.CmsAuthValues); + var mdsResults = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateCaseWitnessesRequest(arg), arg.CmsAuthValues, cancellationToken); return mdsResults; } - public async Task> GetMaterialTypeListAsync(CmsBaseArgDto arg) + public async Task> GetMaterialTypeListAsync(CmsBaseArgDto arg, CancellationToken cancellationToken = default) { - var mdsResults = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateGetMaterialTypeListRequest(arg), arg.CmsAuthValues); + var mdsResults = await CallHttpClientAsync>(_mdsClientRequestFactory.CreateGetMaterialTypeListRequest(arg), arg.CmsAuthValues, cancellationToken); return mdsResults.Select(mdsResult => _cmsMaterialTypeMapper.Map(mdsResult)).ToArray(); } - public async Task> GetWitnessStatementsAsync(MdsWitnessStatementsArgDto arg) + public async Task> GetWitnessStatementsAsync(MdsWitnessStatementsArgDto arg, CancellationToken cancellationToken = default) { - var mdsResults = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetWitnessStatementsRequest(arg), arg.CmsAuthValues); + var mdsResults = await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetWitnessStatementsRequest(arg), arg.CmsAuthValues, cancellationToken); return mdsResults.StatementsForWitness.Select(_caseWitnessStatementMapper.Map).ToArray(); } - public async Task ToggleIsUnusedDocumentAsync(MdsToggleIsUnusedDocumentDto toggleIsUnusedDocumentDto) => - (await CallHttpClientAsync(_mdsClientRequestFactory.CreateToggleIsUnusedDocumentRequest(toggleIsUnusedDocumentDto), toggleIsUnusedDocumentDto.CmsAuthValues)) + public async Task ToggleIsUnusedDocumentAsync(MdsToggleIsUnusedDocumentDto toggleIsUnusedDocumentDto, CancellationToken cancellationToken = default) => + (await CallHttpClientAsync(_mdsClientRequestFactory.CreateToggleIsUnusedDocumentRequest(toggleIsUnusedDocumentDto), toggleIsUnusedDocumentDto.CmsAuthValues, cancellationToken)) .IsSuccessStatusCode; - public async Task> ListCaseIdsAsync(MdsUrnArgDto arg) => - await CallHttpClientAsync>(_mdsClientRequestFactory.CreateListCasesRequest(arg), arg.CmsAuthValues); + public async Task> ListCaseIdsAsync(MdsUrnArgDto arg, CancellationToken cancellationToken = default) => + await CallHttpClientAsync>(_mdsClientRequestFactory.CreateListCasesRequest(arg), arg.CmsAuthValues, cancellationToken); - private async Task GetCaseInternalAsync(MdsCaseIdentifiersArgDto arg) => - await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetCaseRequest(arg), arg.CmsAuthValues); + private async Task GetCaseInternalAsync(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default) => + await CallHttpClientAsync(_mdsClientRequestFactory.CreateGetCaseRequest(arg), arg.CmsAuthValues, cancellationToken); protected override HttpClient GetHttpClient(string cmsAuthValues) { diff --git a/DdeiClient/DdeiClient.csproj b/DdeiClient/DdeiClient.csproj index ed08a699a..5d65fc681 100644 --- a/DdeiClient/DdeiClient.csproj +++ b/DdeiClient/DdeiClient.csproj @@ -16,7 +16,7 @@ - + diff --git a/global.json b/global.json new file mode 100644 index 000000000..05740c38c --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "10.0.300", + "rollForward": "latestFeature" + } +} diff --git a/hkui-functions-integration-tests/hkui-functions-integration-tests.csproj b/hkui-functions-integration-tests/hkui-functions-integration-tests.csproj index a5d42ac66..e8632b0dd 100644 --- a/hkui-functions-integration-tests/hkui-functions-integration-tests.csproj +++ b/hkui-functions-integration-tests/hkui-functions-integration-tests.csproj @@ -11,18 +11,18 @@ HkuiFunctionsIntegrationTests - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/polaris-devops-pipelines/deployments_v2/alerting/alerting-build.yml b/polaris-devops-pipelines/deployments_v2/alerting/alerting-build.yml index c0f49249a..63023762d 100644 --- a/polaris-devops-pipelines/deployments_v2/alerting/alerting-build.yml +++ b/polaris-devops-pipelines/deployments_v2/alerting/alerting-build.yml @@ -5,7 +5,7 @@ trigger: batch: true branches: include: - - main + - development paths: include: - polaris-terraform/alerting-terraform/* diff --git a/polaris-devops-pipelines/deployments_v2/alerting/alerting-pr.yml b/polaris-devops-pipelines/deployments_v2/alerting/alerting-pr.yml index 542de9da9..2279e8b35 100644 --- a/polaris-devops-pipelines/deployments_v2/alerting/alerting-pr.yml +++ b/polaris-devops-pipelines/deployments_v2/alerting/alerting-pr.yml @@ -4,7 +4,7 @@ trigger: none pr: branches: include: - - main + - development paths: include: - polaris-terraform/alerting-terraform/* diff --git a/polaris-devops-pipelines/deployments_v2/networking/networking-build.yml b/polaris-devops-pipelines/deployments_v2/networking/networking-build.yml index c01d0ef02..ecdeebf50 100644 --- a/polaris-devops-pipelines/deployments_v2/networking/networking-build.yml +++ b/polaris-devops-pipelines/deployments_v2/networking/networking-build.yml @@ -5,7 +5,7 @@ trigger: batch: true branches: include: - - main + - development paths: include: - polaris-terraform/networking-terraform/* diff --git a/polaris-devops-pipelines/deployments_v2/networking/networking-pr.yml b/polaris-devops-pipelines/deployments_v2/networking/networking-pr.yml index 72d859a4b..a4cf257b7 100644 --- a/polaris-devops-pipelines/deployments_v2/networking/networking-pr.yml +++ b/polaris-devops-pipelines/deployments_v2/networking/networking-pr.yml @@ -4,7 +4,7 @@ trigger: none pr: branches: include: - - main + - development paths: include: - polaris-terraform/networking-terraform/* diff --git a/polaris-devops-pipelines/deployments_v2/polaris-pr.yml b/polaris-devops-pipelines/deployments_v2/polaris-pr.yml index bebb999e2..57c6cdb6c 100644 --- a/polaris-devops-pipelines/deployments_v2/polaris-pr.yml +++ b/polaris-devops-pipelines/deployments_v2/polaris-pr.yml @@ -4,6 +4,7 @@ trigger: none pr: branches: include: + - development - main paths: include: @@ -37,6 +38,7 @@ stages: parameters: devOpsPatToken: $(System.AccessToken) + #Stage to validate only the parts of Polaris impacted by a pull request, rather than running every validation job on every PR. - stage: Determine_Changes displayName: Determine Changes dependsOn: Wait_For_Running_PRs @@ -45,12 +47,34 @@ stages: pool: name: $(dev-build-agent) steps: + - checkout: self + fetchDepth: 0 + - task: PowerShell@2 inputs: targetType: 'inline' script: | - $files = $(git diff --name-only origin/main) - $temp=$files -split ' ' + $targetBranch = $env:SYSTEM_PULLREQUEST_TARGETBRANCH + $targetBranchOverride = $env:TARGETBRANCHOVERRIDE + + # Optional override to support manual runs. + if ([string]::IsNullOrWhiteSpace($targetBranch) -and -not [string]::IsNullOrWhiteSpace($targetBranchOverride)) + { + $targetBranch = $targetBranchOverride + } + + if ([string]::IsNullOrWhiteSpace($targetBranch)) + { + # Default for non-PR runs when no override is provided. + $targetBranch = "refs/heads/development" + } + + $targetBranchName = $targetBranch -replace '^refs/heads/', '' + echo "Comparing changes against origin/$targetBranchName" + + git fetch origin "+refs/heads/${targetBranchName}:refs/remotes/origin/${targetBranchName}" --depth=200 + + $temp = @(git diff --name-only "origin/$targetBranchName...HEAD" | ForEach-Object { $_.Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) $count=$temp.Length echo "Total changed $count files" diff --git a/polaris-devops-pipelines/deployments_v2/polaris-release-dev.yml b/polaris-devops-pipelines/deployments_v2/polaris-release-dev.yml index 2744a25a2..c8f5c7e58 100644 --- a/polaris-devops-pipelines/deployments_v2/polaris-release-dev.yml +++ b/polaris-devops-pipelines/deployments_v2/polaris-release-dev.yml @@ -9,7 +9,7 @@ resources: trigger: branches: include: - - refs/heads/main + - refs/heads/development stages: - Publish_Artifacts @@ -110,12 +110,13 @@ stages: buildDefinitionId: 588 stageDependsOn: Update_GitHub_Tag - - template: stages/stage_schedule-releases.yml - parameters: - targetBuild: PolarisBuild - targetLabel: "DEV" - appInsightsKey: $(innovation-development-app-insights-instrumentation-key) - environments: - - name: QA - targetLabel: 'QA' - buildDefinitionId: '352' \ No newline at end of file +# Task is no longer required as QA/PROD environments are now deployed via separate branch triggers. The following template is commented out to avoid confusion, but is left in place for reference. + # - template: stages/stage_schedule-releases.yml + # parameters: + # targetBuild: PolarisBuild + # targetLabel: "DEV" + # appInsightsKey: $(innovation-development-app-insights-instrumentation-key) + # environments: + # - name: QA + # targetLabel: 'QA' + # buildDefinitionId: '352' \ No newline at end of file diff --git a/polaris-devops-pipelines/deployments_v2/polaris-triggered-build-uat.yml b/polaris-devops-pipelines/deployments_v2/polaris-triggered-build-uat.yml new file mode 100644 index 000000000..c8558a8bd --- /dev/null +++ b/polaris-devops-pipelines/deployments_v2/polaris-triggered-build-uat.yml @@ -0,0 +1,34 @@ +--- +name: '$(majorMinorVersion).$(Year:yy)$(DayOfYear).$(semanticVersion)-ci' + +trigger: + batch: true + branches: + include: + - main + paths: + include: + - polaris-terraform/main-terraform/* + - polaris-gateway/* + - polaris-maintenance/* + - polaris-pipeline/* + - polaris-ui/* + - polaris-e2e/* + +pr: none + +variables: + - name: buildConfiguration + value: "Release" + - name: majorMinorVersion + value: "1.0" + - name: semanticVersion + value: "$[counter(variables['majorMinorVersion'], 1)]" + +pool: + name: $(build-agent) + +stages: + - template: stages/stage_publish-all-artifacts.yml + parameters: + environment: $(environment) \ No newline at end of file diff --git a/polaris-devops-pipelines/scale-set/build-agent.sh b/polaris-devops-pipelines/scale-set/build-agent.sh index db9579670..9f1ea0fe2 100644 --- a/polaris-devops-pipelines/scale-set/build-agent.sh +++ b/polaris-devops-pipelines/scale-set/build-agent.sh @@ -113,6 +113,7 @@ wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh sudo chmod +x ./dotnet-install.sh ./dotnet-install.sh --channel 6.0 --quality GA ./dotnet-install.sh --channel 7.0 --quality GA +./dotnet-install.sh --channel 10.0 --quality GA echo 'export DOTNET_ROOT=$HOME/.dotnet' >> ~/.bashrc echo 'export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools' >> ~/.bashrc diff --git a/polaris-devops-pipelines/scale-set/deploy-build-agent-pipeline.yml b/polaris-devops-pipelines/scale-set/deploy-build-agent-pipeline.yml index 796e5b7b1..2f3fcd253 100644 --- a/polaris-devops-pipelines/scale-set/deploy-build-agent-pipeline.yml +++ b/polaris-devops-pipelines/scale-set/deploy-build-agent-pipeline.yml @@ -7,7 +7,7 @@ schedules: displayName: Weekly Sunday night build-agent refresh branches: include: - - main + - development always: true variables: diff --git a/polaris-e2e/pipelines/stages/jobs/job_prepare-dot-net-tests.yml b/polaris-e2e/pipelines/stages/jobs/job_prepare-dot-net-tests.yml index 141528293..f594d24bb 100644 --- a/polaris-e2e/pipelines/stages/jobs/job_prepare-dot-net-tests.yml +++ b/polaris-e2e/pipelines/stages/jobs/job_prepare-dot-net-tests.yml @@ -8,6 +8,14 @@ jobs: pool: vmImage: 'windows-latest' steps: + + - task: UseDotNet@2 + displayName: Install .NET SDK 10.x + inputs: + packageType: sdk + version: '10.x' + includePreviewVersions: true + - checkout: ${{ parameters.checkoutRef }} clean: true persistCredentials: true @@ -33,7 +41,7 @@ jobs: - task: FileTransform@2 displayName: "Transform Integration Test Settings" inputs: - folderPath: "$(System.DefaultWorkingDirectory)/polaris-pipeline/pdf-redactor.integration.tests/bin/Release/net8.0" + folderPath: "$(System.DefaultWorkingDirectory)/polaris-pipeline/pdf-redactor.integration.tests/bin/Release/net10.0" jsonTargetFiles: "appSettings.json" xmlTransformationRules: "" # disabled, prevents non-windows-platform error @@ -41,11 +49,11 @@ jobs: inputs: sourceFolder: $(Agent.TempDirectory) contents: Aspose.Total.NET.lic - targetFolder: "$(System.DefaultWorkingDirectory)/polaris-pipeline/pdf-redactor.integration.tests/bin/Release/net8.0" + targetFolder: "$(System.DefaultWorkingDirectory)/polaris-pipeline/pdf-redactor.integration.tests/bin/Release/net10.0" displayName: "Copy Aspose.Total.NET.lic" - task: PublishPipelineArtifact@1 inputs: - targetPath: "$(System.DefaultWorkingDirectory)/polaris-pipeline/pdf-redactor.integration.tests/bin/Release/net8.0" + targetPath: "$(System.DefaultWorkingDirectory)/polaris-pipeline/pdf-redactor.integration.tests/bin/Release/net10.0" artifact: "pdf-redactor-tests-drop" displayName: "Publish PDF Redactor tests artifact" \ No newline at end of file diff --git a/polaris-e2e/pipelines/stages/jobs/job_run-dot-net-tests.yml b/polaris-e2e/pipelines/stages/jobs/job_run-dot-net-tests.yml index a614ff5a4..9c120e8a1 100644 --- a/polaris-e2e/pipelines/stages/jobs/job_run-dot-net-tests.yml +++ b/polaris-e2e/pipelines/stages/jobs/job_run-dot-net-tests.yml @@ -6,6 +6,13 @@ jobs: dependsOn: Prepare_DotNet_Tests steps: + - task: UseDotNet@2 + displayName: Install .NET SDK 10.x + inputs: + packageType: sdk + version: '10.x' + includePreviewVersions: true + - task: NuGetAuthenticate@1 displayName: 'Authenticate to NuGet Feed' diff --git a/polaris-gateway.tests/Functions/HouseKeeping/GetCaseInfoTests.cs b/polaris-gateway.tests/Functions/HouseKeeping/GetCaseInfoTests.cs index f63c5c4f5..6e9d40f6b 100644 --- a/polaris-gateway.tests/Functions/HouseKeeping/GetCaseInfoTests.cs +++ b/polaris-gateway.tests/Functions/HouseKeeping/GetCaseInfoTests.cs @@ -16,7 +16,9 @@ namespace PolarisGateway.Tests.Functions.HouseKeeping; using Common.Dto.Response.HouseKeeping; using Common.Dto.Request; using System; +using System.Threading; using Common.Constants; +using Common.Exceptions; /// /// Unit tests for the class. @@ -53,6 +55,13 @@ public async Task Run_ReturnsOkResult_WhenValidRequestProvided() // Set up a DefaultHttpContext to support setting headers var context = new DefaultHttpContext(); + + // Set up cookies + var mockCookies = new Mock(); + mockCookies.Setup(c => c.TryGetValue(It.IsAny(), out It.Ref.IsAny)) + .Returns(false); + + mockRequest.Setup(r => r.Cookies).Returns(mockCookies.Object); mockRequest.Setup(r => r.HttpContext).Returns(context); mockRequest.Setup(r => r.Headers.Add("corelation", "1232131231")); @@ -60,11 +69,11 @@ public async Task Run_ReturnsOkResult_WhenValidRequestProvided() // Ensure the mock returns the expected list of communications mockCaseInfoService - .Setup(x => x.GetCaseInfoAsync(123, It.IsAny())) + .Setup(x => x.GetCaseInfoAsync(123, It.IsAny(), It.IsAny())) .ReturnsAsync(mockCaseInfo); // Act - IActionResult result = await getCaseInfoFunction.Run(mockRequest.Object, 123); + IActionResult result = await getCaseInfoFunction.Run(mockRequest.Object, 123, CancellationToken.None); // Assert OkObjectResult okResult = Assert.IsType(result); @@ -90,21 +99,51 @@ public async Task Run_ReturnsOkResult_WhenValidRequestProvided() /// /// A task representing the asynchronous test operation. [Fact] + public async Task Run_Returns500InternalError_WhenUnhandledExceptionIsThrown() + { + // Arrange + Mock mockRequest = SetUpMockRequest(); + + mockCaseInfoService + .Setup(x => x.GetCaseInfoAsync(123, It.IsAny(), It.IsAny())) + .ThrowsAsync(new Exception("Internal server error")); + + // Act + IActionResult result = await getCaseInfoFunction.Run(mockRequest.Object, 123, CancellationToken.None); + + // Assert + StatusCodeResult statusCodeResult = Assert.IsType(result); + Assert.Equal(StatusCodes.Status500InternalServerError, statusCodeResult.StatusCode); + + Assert.Contains(mockLogger.Logs, log => + log.LogLevel == LogLevel.Information && + log.Message != null && log.Message.Contains($"{LoggingConstants.HskUiLogPrefix} GetCaseInfo function processed a request.")); + + Assert.Contains(mockLogger.Logs, log => + log.LogLevel == LogLevel.Error && + log.Message != null && log.Message.Contains($"{LoggingConstants.HskUiLogPrefix} GetCaseInfo function encountered an error")); + } + + /// + /// Tests that the function returns an unprocessable entity error when an InvalidOperationException is thrown. + /// + /// A task representing the asynchronous test operation. + [Fact] public async Task Run_ReturnsUnprocessableEntityError_WhenInvalidOperationExceptionIsThrown() { - // Arrange + // Arrange Mock mockRequest = SetUpMockRequest(); mockCaseInfoService - .Setup(x => x.GetCaseInfoAsync(123, It.IsAny())) + .Setup(x => x.GetCaseInfoAsync(123, It.IsAny(), It.IsAny())) .ThrowsAsync(new InvalidOperationException("Invalid operation error")); // Act - IActionResult result = await getCaseInfoFunction.Run(mockRequest.Object, 123); + IActionResult result = await getCaseInfoFunction.Run(mockRequest.Object, 123, CancellationToken.None); // Assert - ObjectResult objectResult = Assert.IsType(result); - Assert.Equal(StatusCodes.Status422UnprocessableEntity, objectResult.StatusCode); + UnprocessableEntityObjectResult unprocessableResult = Assert.IsType(result); + Assert.Equal("Invalid operation error", unprocessableResult.Value); Assert.Contains(mockLogger.Logs, log => log.LogLevel == LogLevel.Information && @@ -112,30 +151,30 @@ public async Task Run_ReturnsUnprocessableEntityError_WhenInvalidOperationExcept Assert.Contains(mockLogger.Logs, log => log.LogLevel == LogLevel.Error && - log.Message != null && log.Message.Contains($"{LoggingConstants.HskUiLogPrefix} GetCaseInfo function encountered an unprocessable entity error: " + - "GetCaseInfo function encountered an error fetching case information for caseId [123]")); + log.Message != null && log.Message.Contains($"{LoggingConstants.HskUiLogPrefix} Invalid operation error")); } /// - /// Tests that the function returns an internal server error when an exception is thrown. + /// Tests that the function returns an unprocessable entity error when an UnprocessableEntityException is thrown. /// /// A task representing the asynchronous test operation. [Fact] - public async Task Run_ReturnsUnprocessableEntityError_WhenExceptionIsThrown() + public async Task Run_ReturnsUnprocessableEntityError_WhenUnprocessableEntityExceptionIsThrown() { - // Arrange + // Arrange Mock mockRequest = SetUpMockRequest(); mockCaseInfoService - .Setup(x => x.GetCaseInfoAsync(123, It.IsAny())) - .ThrowsAsync(new Exception("Unexpected error")); + .Setup(x => x.GetCaseInfoAsync(123, It.IsAny(), It.IsAny())) + .ThrowsAsync(new UnprocessableEntityException("Unprocessable entity error")); // Act - IActionResult result = await getCaseInfoFunction.Run(mockRequest.Object, 123); + IActionResult result = await getCaseInfoFunction.Run(mockRequest.Object, 123, CancellationToken.None); // Assert ObjectResult objectResult = Assert.IsType(result); Assert.Equal(StatusCodes.Status422UnprocessableEntity, objectResult.StatusCode); + Assert.Equal("Unprocessable entity error", objectResult.Value); Assert.Contains(mockLogger.Logs, log => log.LogLevel == LogLevel.Information && @@ -143,8 +182,71 @@ public async Task Run_ReturnsUnprocessableEntityError_WhenExceptionIsThrown() Assert.Contains(mockLogger.Logs, log => log.LogLevel == LogLevel.Error && - log.Message != null && log.Message.Contains($"{LoggingConstants.HskUiLogPrefix} GetCaseInfo function encountered an unprocessable entity error: " + - "GetCaseInfo function encountered an error fetching case information for caseId [123]")); + log.Message != null && log.Message.Contains($"{LoggingConstants.HskUiLogPrefix} GetCaseInfo function encountered an unprocessable entity error: Unprocessable entity error")); + } + + /// + /// Tests that the function properly handles cancellation when the cancellation token is cancelled. + /// + /// A task representing the asynchronous test operation. + [Fact] + public async Task Run_ProperlyCancels_WhenCancellationTokenIsCancelled() + { + // Arrange + Mock mockRequest = SetUpMockRequest(); + var cancellationTokenSource = new CancellationTokenSource(); + + // Set up the service to throw OperationCanceledException when called + mockCaseInfoService + .Setup(x => x.GetCaseInfoAsync(123, It.IsAny(), It.IsAny())) + .ThrowsAsync(new OperationCanceledException("Operation was cancelled")); + + // Act & Assert - expect OperationCanceledException to propagate + var exception = await Assert.ThrowsAnyAsync(async () => + await getCaseInfoFunction.Run(mockRequest.Object, 123, cancellationTokenSource.Token)); + + // Verify the service was called + mockCaseInfoService.Verify( + x => x.GetCaseInfoAsync(123, It.IsAny(), It.IsAny()), + Times.Once); + + // Verify that cancellation was NOT logged (control-flow exceptions should not be logged) + Assert.DoesNotContain(mockLogger.Logs, log => + log.LogLevel == LogLevel.Information && + log.Message != null && log.Message.Contains("was cancelled")); + } + + /// + /// Tests that the cancellation token is passed through to the service layer. + /// + /// A task representing the asynchronous test operation. + [Fact] + public async Task Run_PassesCancellationToken_ToServiceLayer() + { + // Arrange + Mock mockRequest = SetUpMockRequest(); + var cancellationTokenSource = new CancellationTokenSource(); + var mockCaseInfo = new CaseSummaryResponse(123, "06SC1234572", "Will", "SMITH", 2, "Hull UT"); + + CancellationToken capturedToken = default; + + mockCaseInfoService + .Setup(x => x.GetCaseInfoAsync(123, It.IsAny(), It.IsAny())) + .Callback((caseId, authValues, ct) => + { + capturedToken = ct; + }) + .ReturnsAsync(mockCaseInfo); + + // Act + await getCaseInfoFunction.Run(mockRequest.Object, 123, cancellationTokenSource.Token); + + // Assert + Assert.Equal(cancellationTokenSource.Token, capturedToken); + + mockCaseInfoService.Verify( + x => x.GetCaseInfoAsync(123, It.IsAny(), cancellationTokenSource.Token), + Times.Once); } private static Mock SetUpMockRequest() @@ -153,6 +255,13 @@ private static Mock SetUpMockRequest() // Set up a DefaultHttpContext to support setting headers var context = new DefaultHttpContext(); + + // Set up cookies + var mockCookies = new Mock(); + mockCookies.Setup(c => c.TryGetValue(It.IsAny(), out It.Ref.IsAny)) + .Returns(false); + + mockRequest.Setup(r => r.Cookies).Returns(mockCookies.Object); mockRequest.Setup(r => r.HttpContext).Returns(context); mockRequest.Setup(r => r.Headers.Add("corelation", "1232131231")); diff --git a/polaris-gateway.tests/Functions/HouseKeeping/GetCaseMaterialsTests.cs b/polaris-gateway.tests/Functions/HouseKeeping/GetCaseMaterialsTests.cs index 62fa4776e..cfee952d0 100644 --- a/polaris-gateway.tests/Functions/HouseKeeping/GetCaseMaterialsTests.cs +++ b/polaris-gateway.tests/Functions/HouseKeeping/GetCaseMaterialsTests.cs @@ -1661,6 +1661,132 @@ public async Task Run_ReturnsOkResult_WhenUsedMgFormsAreEmpty() log.Message != null && log.Message.Contains($"{LoggingConstants.HskUiLogPrefix} Milestone: caseId [123] GetCaseMaterials function completed")); } + /// + /// Tests that PE3 (1053), PE4 (1054), and DREP (1055) material types are excluded from Used MG Forms. + /// + /// A task representing the asynchronous test operation. + [Fact] + public async Task Run_ExcludesPE3PE4AndDREPMaterialTypes_FromUsedMgForms() + { + // Arrange + var mockRequest = SetUpMockRequest(); + + var mockCommunications = new List + { + new Communication(1, "FileA.pdf", "Subject A", 1012, 123, "/some/path/doc1.pdf", "None", "Administrative", "Type A", false), + }; + + var mockCaseMaterials = new List + { + new CaseMaterial(1, "FileA.pdf", "Subject A", 1012, 123, "/some/path/doc1.pdf", "Administrative", "Type A", false, "None"), + }; + + // Create Used MG Forms with both excluded and included material types + var mgFormPE3 = new MgForm( + Id: 1, + Title: "PE3 Form", + OriginalFileName: "pe3.pdf", + MaterialType: "1053", // PE3 - should be excluded + DocumentType: 1053, + Link: "http://example1.com", + Status: "Used", + Date: receivedDate); + + var mgFormPE4 = new MgForm( + Id: 2, + Title: "PE4 Form", + OriginalFileName: "pe4.pdf", + MaterialType: "1054", // PE4 - should be excluded + DocumentType: 1054, + Link: "http://example2.com", + Status: "Used", + Date: receivedDate); + + var mgFormDREP = new MgForm( + Id: 3, + Title: "DREP Form", + OriginalFileName: "drep.pdf", + MaterialType: "1055", // DREP - should be excluded + DocumentType: 1055, + Link: "http://example3.com", + Status: "Used", + Date: receivedDate); + + var mgFormValid = new MgForm( + Id: 4, + Title: "Valid MG Form", + OriginalFileName: "valid.pdf", + MaterialType: "1202", // Valid - should NOT be excluded + DocumentType: 1202, + Link: "http://example4.com", + Status: "Used", + Date: receivedDate); + + var usedMgForms = new UsedMgFormsResponse + { + MgForms = new List { mgFormPE3, mgFormPE4, mgFormDREP, mgFormValid }, + }; + + // Only the valid MG form should be mapped (PE3, PE4, DREP excluded) + var mappedUsedMgFormsCaseMaterials = new List + { + new CaseMaterial(4, "valid.pdf", "Valid MG Form", 1202, 4, "http://example4.com", "MG Form", "MG Form", false, "Used"), + }; + + // Mock empty responses for other materials + var unusedMaterials = new UnusedMaterialsResponse(); + var usedStatements = new Common.Dto.Response.HouseKeeping.UsedStatementsResponse(); + var usedExhibits = new UsedExhibitsResponse(); + var usedOtherMaterials = new UsedOtherMaterialsResponse(); + var exhibitProducers = new ExhibitProducersResponse(); + + // Mock RetrieveCaseMaterialsAsync + mockCaseMaterialService + .Setup(x => x.RetrieveCaseMaterialsAsync(123, It.IsAny())) + .ReturnsAsync((mockCommunications, unusedMaterials, usedStatements, usedExhibits, usedMgForms, usedOtherMaterials, exhibitProducers)); + + // Mock MapCommunicationsToCaseMaterials + mockCaseMaterialService + .Setup(x => x.MapCommunicationsToCaseMaterials(mockCommunications)) + .Returns(mockCaseMaterials); + + // Mock MapUsedMgFormsToCaseMaterials - should only receive the valid MG form after filtering + mockCaseMaterialService + .Setup(x => x.MapUsedMgFormsToCaseMaterials(It.IsAny())) + .Returns(response => + { + // Verify that PE3, PE4, and DREP were filtered out + Assert.Single(response.MgForms); + Assert.Equal("1202", response.MgForms[0].MaterialType); + Assert.Equal("Valid MG Form", response.MgForms[0].Title); + return mappedUsedMgFormsCaseMaterials; + }); + + // Act + IActionResult result = await getCaseMaterialsFunction.Run(mockRequest.Object, 123); + + // Assert + OkObjectResult okResult = Assert.IsType(result); + Assert.NotNull(okResult.Value); + + List caseMaterials = Assert.IsType>(okResult.Value); + + // Verify the result contains communications + the valid MG form (PE3, PE4, DREP excluded) + Assert.Equal(2, caseMaterials.Count); + Assert.Contains(caseMaterials, cm => cm.Id == 1 && cm.Subject == "Subject A"); + Assert.Contains(caseMaterials, cm => cm.Id == 4 && cm.Subject == "Valid MG Form"); + + // Verify PE3, PE4, and DREP are NOT in the result + Assert.DoesNotContain(caseMaterials, cm => cm.Subject == "PE3 Form"); + Assert.DoesNotContain(caseMaterials, cm => cm.Subject == "PE4 Form"); + Assert.DoesNotContain(caseMaterials, cm => cm.Subject == "DREP Form"); + + // Verify the MapUsedMgFormsToCaseMaterials was called once + mockCaseMaterialService.Verify( + x => x.MapUsedMgFormsToCaseMaterials(It.IsAny()), + Times.Once); + } + private static Mock SetUpMockRequest() { var mockRequest = new Mock(); diff --git a/polaris-gateway.tests/Services/Artefact/CacheServiceTests.cs b/polaris-gateway.tests/Services/Artefact/CacheServiceTests.cs index b68f4a977..14cfbd734 100644 --- a/polaris-gateway.tests/Services/Artefact/CacheServiceTests.cs +++ b/polaris-gateway.tests/Services/Artefact/CacheServiceTests.cs @@ -104,11 +104,11 @@ public async Task UploadPdfAsync_WhenUploadingPdf_WillUploadPdf() var pdfStream = new MemoryStream(_fixture.Create()); // Act - await _cacheService.UploadPdfAsync(_caseId, _documentId, _versionId, _isOcrProcessed, pdfStream); + await _cacheService.UploadPdfAsync(_caseId, _documentId, _versionId, _isOcrProcessed, pdfStream, It.IsAny()); // Assert _polarisBlobStorageServiceMock - .Verify(x => x.UploadBlobAsync(pdfStream, _blobIdType, _isOcrProcessed), Times.Once); + .Verify(x => x.UploadBlobAsync(pdfStream, _blobIdType, _isOcrProcessed, It.IsAny()), Times.Once); } [Fact] diff --git a/polaris-gateway.tests/Services/Artefact/OcrArtefactServiceTests.cs b/polaris-gateway.tests/Services/Artefact/OcrArtefactServiceTests.cs index 9011745c5..b72072e1d 100644 --- a/polaris-gateway.tests/Services/Artefact/OcrArtefactServiceTests.cs +++ b/polaris-gateway.tests/Services/Artefact/OcrArtefactServiceTests.cs @@ -161,7 +161,13 @@ public async Task GetOcrAsync_WhenOperationIdIsNotProvidedAndPdfIsNotAvailable_W }; _pdfArtefactServiceMock - .Setup(x => x.GetPdfAsync(_cmsAuthValues, _correlationId, _urn, _caseId, _documentId, _versionId, false, false)) + .Setup(x => x.GetPdfAsync(It.Is(r => + r.Urn == _urn && + r.CaseId == _caseId && + r.MaterialId == _documentId && + r.DocumentId == _versionId && + r.IsOcrProcessed == false && + r.ForceRefresh == false), _cmsAuthValues, _correlationId, default)) .ReturnsAsync(pdfResult); var expectedResult = new ArtefactResult(); @@ -192,7 +198,13 @@ public async Task GetOcrAsync_WhenOperationIdIsNotProvidedAndPdfIsAvailable_Will }; _pdfArtefactServiceMock - .Setup(x => x.GetPdfAsync(_cmsAuthValues, _correlationId, _urn, _caseId, _documentId, _versionId, false, false)) + .Setup(x => x.GetPdfAsync(It.Is(r => + r.Urn == _urn && + r.CaseId == _caseId && + r.MaterialId == _documentId && + r.DocumentId == _versionId && + r.IsOcrProcessed == false && + r.ForceRefresh == false), _cmsAuthValues, _correlationId, default)) .ReturnsAsync(pdfResult); var newOperationId = _fixture.Create(); diff --git a/polaris-gateway.tests/Services/Artefact/PdfArtefactServiceTests.cs b/polaris-gateway.tests/Services/Artefact/PdfArtefactServiceTests.cs index e6ca4a095..48de4ed7c 100644 --- a/polaris-gateway.tests/Services/Artefact/PdfArtefactServiceTests.cs +++ b/polaris-gateway.tests/Services/Artefact/PdfArtefactServiceTests.cs @@ -2,14 +2,19 @@ using AutoFixture; using Common.Constants; using Common.Domain.Ocr; -using Common.Services.OcrService; using Common.Services.BlobStorage; +using Common.Services.OcrService; using FluentAssertions; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; using Moq; +using Microsoft.Extensions.Options; +using PolarisGateway.Models; using PolarisGateway.Services.Artefact; using PolarisGateway.Services.Artefact.Domain; using PolarisGateway.Services.Artefact.Factories; using System; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using Xunit; @@ -47,6 +52,8 @@ public PdfArtefactServiceTests() _ocrServiceMock = new Mock(); _pdfArtefactService = new PdfArtefactService( + Options.Create(new RedactionFileSizeOptions { FileSizeLimitMb = 10 }), + new Mock>().Object, _cacheServiceMock.Object, _artefactServiceResponseFactoryMock.Object, _pdfRetrievalServiceMock.Object, @@ -71,7 +78,13 @@ public async Task GetPdfAsync_WhenAPdfIsAlreadyInCache_WillReturnCachedPdf() .Returns(expectedResult); // Act - var result = await _pdfArtefactService.GetPdfAsync(_cmsAuthValues, _correlationId, _urn, _caseId, _documentId, _versionId, false); + var request = new GetPdfRequest( + Urn: _urn, + CaseId: _caseId, + MaterialId: _documentId, + DocumentId: _versionId, + IsOcrProcessed: false); + var result = await _pdfArtefactService.GetPdfAsync(request, _cmsAuthValues, _correlationId); // Assert result.Should().Be(expectedResult); @@ -96,7 +109,13 @@ public async Task GetPdfAsync_WhenAPdfIsNotAlreadyInCacheAndConversionFails_Will .Returns(expectedResult); // Act - var result = await _pdfArtefactService.GetPdfAsync(_cmsAuthValues, _correlationId, _urn, _caseId, _documentId, _versionId, false); + var request = new GetPdfRequest( + Urn: _urn, + CaseId: _caseId, + MaterialId: _documentId, + DocumentId: _versionId, + IsOcrProcessed: false); + var result = await _pdfArtefactService.GetPdfAsync(request, _cmsAuthValues, _correlationId); // Assert result.Should().Be(expectedResult); @@ -114,7 +133,7 @@ public async Task GetPdfAsync_WhenAPdfIsNotAlreadyInCacheAndConversionSucceeds_W .ReturnsAsync((true, pdfStream)); _cacheServiceMock - .Setup(x => x.UploadPdfAsync(_caseId, _documentId, _versionId, false, It.IsAny())) + .Setup(x => x.UploadPdfAsync(_caseId, _documentId, _versionId, false, It.IsAny(), It.IsAny())) .Returns(Task.CompletedTask); var fakeOcrOperationId = Guid.NewGuid(); @@ -148,10 +167,157 @@ public async Task GetPdfAsync_WhenAPdfIsNotAlreadyInCacheAndConversionSucceeds_W .Returns(expectedResult); // Act - var result = await _pdfArtefactService.GetPdfAsync(_cmsAuthValues, _correlationId, _urn, _caseId, _documentId, _versionId, false); + var request = new GetPdfRequest( + Urn: _urn, + CaseId: _caseId, + MaterialId: _documentId, + DocumentId: _versionId, + IsOcrProcessed: false); + var result = await _pdfArtefactService.GetPdfAsync(request, _cmsAuthValues, _correlationId); // Assert result.Should().Be(expectedResult); - _cacheServiceMock.Verify(x => x.UploadPdfAsync(_caseId, _documentId, _versionId, false, It.IsAny()), Times.Once); + _cacheServiceMock.Verify(x => x.UploadPdfAsync(_caseId, _documentId, _versionId, false, It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public async Task GetPdfAsync_WhenCachedPdfExceedsSizeLimit_ShouldReturnLargeFileFlag() + { + // Arrange + var largeStream = new MemoryStream(new byte[20 * 1024 * 1024]); // 20 MB + + _cacheServiceMock + .Setup(x => x.TryGetPdfAsync(_caseId, _documentId, _versionId, false)) + .ReturnsAsync((true, (Stream)largeStream)); + + _cacheServiceMock + .Setup(x => x.GetPdfSizeFromMetadataAsync(_caseId, _documentId, _versionId, false)) + .ReturnsAsync(20); // MB (above limit of 10) + + var expectedResult = new ArtefactResult(); + + _artefactServiceResponseFactoryMock + .Setup(x => x.CreateOkResultWithLargeFileFlag(It.IsAny(), true, true)) + .Returns(expectedResult); + + // Act + var request = new GetPdfRequest( + Urn: _urn, + CaseId: _caseId, + MaterialId: _documentId, + DocumentId: _versionId, + IsOcrProcessed: false); + var result = await _pdfArtefactService.GetPdfAsync(request, _cmsAuthValues, _correlationId); + + // Assert + result.Should().Be(expectedResult); + + _artefactServiceResponseFactoryMock.Verify( + x => x.CreateOkResultWithLargeFileFlag(It.IsAny(), true, true), + Times.Once); + } + + [Fact] + public async Task GetPdfAsync_WhenCachedPdfWithinLimit_ShouldReturnNormalResult() + { + // Arrange + var smallStream = new MemoryStream(new byte[1 * 1024 * 1024]); // 1 MB + + _cacheServiceMock + .Setup(x => x.TryGetPdfAsync(_caseId, _documentId, _versionId, false)) + .ReturnsAsync((true, (Stream)smallStream)); + + _cacheServiceMock + .Setup(x => x.GetPdfSizeFromMetadataAsync(_caseId, _documentId, _versionId, false)) + .ReturnsAsync(1); // below limit + + var expectedResult = new ArtefactResult(); + + _artefactServiceResponseFactoryMock + .Setup(x => x.CreateOkfResult(It.IsAny(), true)) + .Returns(expectedResult); + + // Act + var request = new GetPdfRequest( + Urn: _urn, + CaseId: _caseId, + MaterialId: _documentId, + DocumentId: _versionId, + IsOcrProcessed: false); + var result = await _pdfArtefactService.GetPdfAsync(request, _cmsAuthValues, _correlationId); + + // Assert + result.Should().Be(expectedResult); + } + + [Fact] + public async Task GetPdfAsync_WhenNewPdfExceedsSizeLimit_ShouldReturnLargeFileFlag() + { + // Arrange + var largeBytes = new byte[20 * 1024 * 1024]; + var stream = new MemoryStream(largeBytes); + + _cacheServiceMock + .SetupSequence(x => x.TryGetPdfAsync(_caseId, _documentId, _versionId, false)) + .ReturnsAsync((false, null)) + .ReturnsAsync((true, (Stream)stream)); + + _pdfRetrievalServiceMock + .Setup(x => x.GetPdfStreamAsync( + _cmsAuthValues, _correlationId, _urn, + _caseId, _documentId, _versionId)) + .ReturnsAsync(new DocumentRetrievalResult + { + Status = PdfConversionStatus.DocumentConverted, + PdfStream = new MemoryStream(largeBytes) + }); + + _cacheServiceMock + .Setup(x => x.UploadPdfAsync( + _caseId, _documentId, _versionId, + false, It.IsAny(), It.IsAny())) + .Returns(Task.CompletedTask); + + var fakeOcrOperationId = Guid.NewGuid(); + + _ocrServiceMock + .Setup(x => x.InitiateOperationAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(fakeOcrOperationId); + + _ocrServiceMock + .Setup(x => x.GetOperationResultsAsync(fakeOcrOperationId, It.IsAny())) + .ReturnsAsync(new OcrOperationResult + { + IsSuccess = true, + AnalyzeResults = new AnalyzeResults() + }); + + _cacheServiceMock + .Setup(x => x.UploadJsonObjectAsync( + _caseId, _documentId, _versionId, + BlobType.Ocr, It.IsAny())) + .Returns(Task.CompletedTask); + + var expectedResult = new ArtefactResult(); + + _artefactServiceResponseFactoryMock + .Setup(x => x.CreateOkResultWithLargeFileFlag(It.IsAny(), false, true)) + .Returns(expectedResult); + + // Act + var request = new GetPdfRequest( + Urn: _urn, + CaseId: _caseId, + MaterialId: _documentId, + DocumentId: _versionId, + IsOcrProcessed: false); + var result = await _pdfArtefactService.GetPdfAsync(request, _cmsAuthValues, _correlationId); + + // Assert + result.Should().Be(expectedResult); + + _artefactServiceResponseFactoryMock.Verify( + x => x.CreateOkResultWithLargeFileFlag(It.IsAny(), false, true), + Times.Once); } -} \ No newline at end of file +} diff --git a/polaris-gateway.tests/polaris-gateway.tests.csproj b/polaris-gateway.tests/polaris-gateway.tests.csproj index 0ccd78d6b..751462ef1 100644 --- a/polaris-gateway.tests/polaris-gateway.tests.csproj +++ b/polaris-gateway.tests/polaris-gateway.tests.csproj @@ -19,22 +19,22 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/polaris-gateway/ApplicationStartup/ServiceExtensions.cs b/polaris-gateway/ApplicationStartup/ServiceExtensions.cs index 1fbd7bf89..efe64b566 100644 --- a/polaris-gateway/ApplicationStartup/ServiceExtensions.cs +++ b/polaris-gateway/ApplicationStartup/ServiceExtensions.cs @@ -1,37 +1,38 @@ -using System.Net; -using System.Net.Http.Headers; +using Common.Clients.PdfGenerator; +using Common.Factories.ComputerVisionClientFactory; +using Common.Mappers; +using Common.Services.BlobStorage; +using Common.Services.DocumentToggle; +using Common.Services.OcrService; +using Common.Services.PiiService; +using Common.Telemetry; +using Common.Wrappers; +using Cps.Fct.Hk.Ui.Interfaces; +using Cps.Fct.Hk.Ui.ServiceClient.Uma; +using Cps.Fct.Hk.Ui.ServiceClient.Uma.Configuration; +using Cps.Fct.Hk.Ui.Services; +using Cps.Fct.Hk.Ui.Services.Validators; +using Ddei.Extensions; +using DdeiClient.Clients; +using DdeiClient.Clients.Interfaces; +using DdeiClient.Configuration; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; -using Common.Telemetry; -using Common.Wrappers; using PolarisGateway.Clients.Coordinator; +using PolarisGateway.Clients.PdfThumbnailGenerator; using PolarisGateway.Mappers; +using PolarisGateway.Models; +using PolarisGateway.Services.Artefact; +using PolarisGateway.Services.MdsOrchestration; using PolarisGateway.Validators; using Polly; using Polly.Contrib.WaitAndRetry; -using Ddei.Extensions; -using Common.Services.DocumentToggle; -using Common.Services.OcrService; -using Common.Factories.ComputerVisionClientFactory; -using Common.Clients.PdfGenerator; -using Common.Services.BlobStorage; -using Common.Services.PiiService; -using PolarisGateway.Clients.PdfThumbnailGenerator; -using PolarisGateway.Services.Artefact; -using PolarisGateway.Services.MdsOrchestration; -using System.Net.Http; using System; -using Cps.Fct.Hk.Ui.Interfaces; -using Cps.Fct.Hk.Ui.Services; -using DdeiClient.Clients.Interfaces; -using DdeiClient.Clients; -using DdeiClient.Configuration; -using Common.Mappers; -using Cps.Fct.Hk.Ui.Services.Validators; -using Cps.Fct.Hk.Ui.ServiceClient.Uma; -using Cps.Fct.Hk.Ui.ServiceClient.Uma.Configuration; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; namespace PolarisGateway.ApplicationStartup; @@ -77,6 +78,8 @@ public static IServiceCollection ConfigureServices(this IServiceCollection servi services.AddPiiService(); services.AddArtefactService(); services.AddMdsOrchestrationService(); + services.Configure(configuration.GetSection(RedactionFileSizeOptions.ConfigKey)); + // House keeping. services.AddSingleton(); diff --git a/polaris-gateway/Functions/AddDocumentNote.cs b/polaris-gateway/Functions/AddDocumentNote.cs index e74af2399..65003bc1c 100644 --- a/polaris-gateway/Functions/AddDocumentNote.cs +++ b/polaris-gateway/Functions/AddDocumentNote.cs @@ -15,6 +15,7 @@ using PolarisGateway.TelemetryEvents; using PolarisGateway.Validators; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -51,7 +52,8 @@ public AddDocumentNote( public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.DocumentNotes)] HttpRequest req, string caseUrn, int caseId, - string materialId) + string materialId, + CancellationToken cancellationToken = default) { var telemetryEvent = new DocumentNoteRequestEvent(caseId, materialId) { diff --git a/polaris-gateway/Functions/CancelCheckoutDocument.cs b/polaris-gateway/Functions/CancelCheckoutDocument.cs index 80d7e65d2..7ee226c27 100644 --- a/polaris-gateway/Functions/CancelCheckoutDocument.cs +++ b/polaris-gateway/Functions/CancelCheckoutDocument.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -43,7 +44,7 @@ public CancelCheckoutDocument( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = RestApi.DocumentCheckout)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId) + [HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = RestApi.DocumentCheckout)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); @@ -56,7 +57,7 @@ public async Task Run( materialId: materialId, documentId: documentId); - await _mdsClient.CancelCheckoutDocumentAsync(mdsDocumentIdAndVersionIdArgDto); + await _mdsClient.CancelCheckoutDocumentAsync(mdsDocumentIdAndVersionIdArgDto, cancellationToken: cancellationToken); return new OkResult(); } diff --git a/polaris-gateway/Functions/CheckoutDocument.cs b/polaris-gateway/Functions/CheckoutDocument.cs index 3e8746ec4..b3ad72f5a 100644 --- a/polaris-gateway/Functions/CheckoutDocument.cs +++ b/polaris-gateway/Functions/CheckoutDocument.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -40,7 +41,7 @@ public CheckoutDocument( [OpenApiParameter("materialId", In = ParameterLocation.Path, Type = typeof(long), Description = "The document Id (version) of the material which has to be checked out", Required = true)] [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(object), Summary = "Case found", Description = "Returns case details")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.DocumentCheckout)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.DocumentCheckout)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); @@ -53,7 +54,7 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, materialId: materialId, documentId: documentId); - await _mdsClient.CheckoutDocumentAsync(mdsDocumentIdAndVersionIdArgDto); + await _mdsClient.CheckoutDocumentAsync(mdsDocumentIdAndVersionIdArgDto, cancellationToken); return new OkResult(); } } \ No newline at end of file diff --git a/polaris-gateway/Functions/GenerateThumbnail.cs b/polaris-gateway/Functions/GenerateThumbnail.cs index d2899c06b..55747074c 100644 --- a/polaris-gateway/Functions/GenerateThumbnail.cs +++ b/polaris-gateway/Functions/GenerateThumbnail.cs @@ -12,6 +12,7 @@ using PolarisGateway.Extensions; using System; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -47,8 +48,9 @@ public GenerateThumbnail( [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(AddDocumentNoteRequestDto), Summary = "Case found", Description = "Returns case details")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.GenerateThumbnail)] HttpRequest req, - string caseUrn, int caseId, string materialId, int documentId, int maxDimensionPixel, int? pageIndex) + string caseUrn, int caseId, string materialId, int documentId, int maxDimensionPixel, int? pageIndex, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); diff --git a/polaris-gateway/Functions/GetCase.cs b/polaris-gateway/Functions/GetCase.cs index ac6c4de78..45be54f3a 100644 --- a/polaris-gateway/Functions/GetCase.cs +++ b/polaris-gateway/Functions/GetCase.cs @@ -12,6 +12,7 @@ using Microsoft.OpenApi.Models; using System; using System.Net; +using System.Threading; using PolarisGateway.Services.MdsOrchestration; using System.Threading.Tasks; @@ -43,7 +44,7 @@ public GetCase( [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK,contentType: "application/json",bodyType: typeof(object),Summary = "Case found",Description = "Returns case details")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request",Description = "Missing or invalid parameters")] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Case)] HttpRequest req, string caseUrn, int caseId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Case)] HttpRequest req, string caseUrn, int caseId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); @@ -51,7 +52,7 @@ public async Task Run( var arg = _mdsArgFactory.CreateCaseIdentifiersArg(cmsAuthValues, correlationId, caseUrn, caseId); - var result = await _mdsOrchestrationService.GetCase(arg); + var result = await _mdsOrchestrationService.GetCase(arg, cancellationToken); return new OkObjectResult(result); } diff --git a/polaris-gateway/Functions/GetCases.cs b/polaris-gateway/Functions/GetCases.cs index 7f05bcee2..39efa4f6c 100644 --- a/polaris-gateway/Functions/GetCases.cs +++ b/polaris-gateway/Functions/GetCases.cs @@ -11,6 +11,7 @@ using Microsoft.OpenApi.Models; using System.Collections.Generic; using System.Net; +using System.Threading; using PolarisGateway.Services.MdsOrchestration; using System.Threading.Tasks; @@ -41,14 +42,14 @@ public GetCases( [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(IEnumerable), Summary = "List of cases for a case URN", Description = "Returns case list for the given URN")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Cases)] HttpRequest req, string caseUrn) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Cases)] HttpRequest req, string caseUrn, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); var arg = _mdsArgFactory.CreateUrnArg(cmsAuthValues, correlationId, caseUrn); - var result = await _mdsOrchestrationService.GetCases(arg); + var result = await _mdsOrchestrationService.GetCases(arg, cancellationToken); return new OkObjectResult(result); } diff --git a/polaris-gateway/Functions/GetDocumentList.cs b/polaris-gateway/Functions/GetDocumentList.cs index 1a9d02f31..0923d5f24 100644 --- a/polaris-gateway/Functions/GetDocumentList.cs +++ b/polaris-gateway/Functions/GetDocumentList.cs @@ -13,6 +13,7 @@ using System.Collections; using System.Collections.Generic; using System.Net; +using System.Threading; using Ddei.Factories; using Microsoft.Azure.Functions.Worker; using System.Threading.Tasks; @@ -50,10 +51,11 @@ public GetDocumentList( [OpenApiParameter("caseId", In = ParameterLocation.Path, Type = typeof(int), Description = "The Id of the case.", Required = true)] [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(IEnumerable), Summary = "Document List", Description = "Returns list of documennts")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Documents)] HttpRequest req, string caseUrn, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Documents)] HttpRequest req, string caseUrn, int caseId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); + cancellationToken.ThrowIfCancellationRequested(); var arg = _mdsArgFactory.CreateCaseIdentifiersArg(cmsAuthValues, correlationId, caseUrn, caseId); var result = await _mdsOrchestrationService.GetCaseDocuments(arg); diff --git a/polaris-gateway/Functions/GetDocumentNotes.cs b/polaris-gateway/Functions/GetDocumentNotes.cs index 9ce2cc84e..487066c52 100644 --- a/polaris-gateway/Functions/GetDocumentNotes.cs +++ b/polaris-gateway/Functions/GetDocumentNotes.cs @@ -12,6 +12,7 @@ using Microsoft.OpenApi.Models; using System.Collections.Generic; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -41,14 +42,14 @@ public GetDocumentNotes(ILogger logger, [OpenApiParameter("materialId", In = ParameterLocation.Path, Type = typeof(string), Description = "The Id of the material", Required = true)] [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(IEnumerable), Summary = "Document Note List", Description = "Returns list of document notes")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.DocumentNotes)] HttpRequest req, string caseUrn, int caseId, string materialId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.DocumentNotes)] HttpRequest req, string caseUrn, int caseId, string materialId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); var arg = _mdsArgFactory.CreateDocumentArgDto(cmsAuthValues, correlationId, caseUrn, caseId, materialId); - var result = await _mdsClient.GetDocumentNotesAsync(arg); + var result = await _mdsClient.GetDocumentNotesAsync(arg, cancellationToken: cancellationToken); return new OkObjectResult(result); } diff --git a/polaris-gateway/Functions/GetExhibitProducers.cs b/polaris-gateway/Functions/GetExhibitProducers.cs index 21482cfdc..0e2a2527c 100644 --- a/polaris-gateway/Functions/GetExhibitProducers.cs +++ b/polaris-gateway/Functions/GetExhibitProducers.cs @@ -13,6 +13,7 @@ using System.Collections; using System.Collections.Generic; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -41,14 +42,14 @@ public GetExhibitProducers(ILogger logger, [OpenApiParameter("caseId", In = ParameterLocation.Path, Type = typeof(int), Description = "The Id of the case to add a new action plan.", Required = true)] [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(IEnumerable), Summary = "Case exhibit producers", Description = "Returns the list of exhibit producers")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseExhibitProducers)] HttpRequest req, string caseUrn, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseExhibitProducers)] HttpRequest req, string caseUrn, int caseId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); var mdsCaseIdentifiersArgDto = _mdsArgFactory.CreateCaseIdentifiersArg(cmsAuthValues, correlationId, caseUrn, caseId); - var exhibitProducerDtos = await _mdsClient.GetExhibitProducersAsync(mdsCaseIdentifiersArgDto); + var exhibitProducerDtos = await _mdsClient.GetExhibitProducersAsync(mdsCaseIdentifiersArgDto, cancellationToken: cancellationToken); return new OkObjectResult(exhibitProducerDtos); } diff --git a/polaris-gateway/Functions/GetMaterialTypeList.cs b/polaris-gateway/Functions/GetMaterialTypeList.cs index 0d2757c7b..8defbb733 100644 --- a/polaris-gateway/Functions/GetMaterialTypeList.cs +++ b/polaris-gateway/Functions/GetMaterialTypeList.cs @@ -13,6 +13,7 @@ using Microsoft.OpenApi.Models; using System.Collections.Generic; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -40,13 +41,13 @@ public GetMaterialTypeList( [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(IEnumerable), Summary = "Material Type List", Description = "Returns the list of material types")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.MaterialTypeList)] HttpRequest req) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.MaterialTypeList)] HttpRequest req, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); var mdsBaseArgDto = _mdsArgFactory.CreateCmsCaseDataArgDto(cmsAuthValues, correlationId); - var materialTypes = await _mdsClient.GetMaterialTypeListAsync(mdsBaseArgDto); + var materialTypes = await _mdsClient.GetMaterialTypeListAsync(mdsBaseArgDto, cancellationToken); return new OkObjectResult(materialTypes); } diff --git a/polaris-gateway/Functions/GetOcr.cs b/polaris-gateway/Functions/GetOcr.cs index af3a6921b..0b7956e2b 100644 --- a/polaris-gateway/Functions/GetOcr.cs +++ b/polaris-gateway/Functions/GetOcr.cs @@ -8,6 +8,7 @@ using PolarisGateway.Services.Artefact.Domain; using System; using System.Net; +using System.Threading; using System.Threading.Tasks; using Common.Extensions; using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; @@ -46,8 +47,9 @@ public GetOcr( [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(ArtefactResult), Summary = "Artefact Result", Description = "Gives the artefact results")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Ocr)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Ocr)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); diff --git a/polaris-gateway/Functions/GetPdf.cs b/polaris-gateway/Functions/GetPdf.cs index 8661f009c..11d532efd 100644 --- a/polaris-gateway/Functions/GetPdf.cs +++ b/polaris-gateway/Functions/GetPdf.cs @@ -1,5 +1,6 @@ using Common.Configuration; using Common.Extensions; +using Common.Constants; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; @@ -11,6 +12,7 @@ using PolarisGateway.Services.Artefact.Domain; using System.IO; using System.Net; +using System.Threading; using System.Threading.Tasks; @@ -46,14 +48,30 @@ public GetPdf( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Pdf)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Pdf)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); var isOcrProcessed = req.Query.ContainsKey(isOcrProcessedParamName) && bool.Parse(req.Query[isOcrProcessedParamName]); var forceRefresh = req.Query.ContainsKey(ForceRefreshParamName) && bool.Parse(req.Query[ForceRefreshParamName]); - var getPdfResult = await _pdfArtefactService.GetPdfAsync(cmsAuthValues, correlationId, caseUrn, caseId, materialId, documentId, isOcrProcessed, forceRefresh); + + var request = new GetPdfRequest( + Urn: caseUrn, + CaseId: caseId, + MaterialId: materialId, + DocumentId: documentId, + IsOcrProcessed: isOcrProcessed, + ForceRefresh: forceRefresh); + + var getPdfResult = await _pdfArtefactService.GetPdfAsync(request, cmsAuthValues, correlationId, cancellationToken); + + if (getPdfResult.FileSizeExceedsLimit == true) + { + req.HttpContext.Response.Headers.Append(HttpHeaderKeys.AccessControlExposeHeaders, HttpHeaderKeys.CpsFileTooLarge); + req.HttpContext.Response.Headers[HttpHeaderKeys.CpsFileTooLarge] = "true"; + } + return getPdfResult.Status == ResultStatus.ArtefactAvailable ? new FileStreamResult(getPdfResult.Artefact, PdfContentType) : new JsonResult(getPdfResult) diff --git a/polaris-gateway/Functions/GetPii.cs b/polaris-gateway/Functions/GetPii.cs index f7f332bbd..a003008b1 100644 --- a/polaris-gateway/Functions/GetPii.cs +++ b/polaris-gateway/Functions/GetPii.cs @@ -17,6 +17,7 @@ using System.Collections; using System.Collections.Generic; using System.Net; +using System.Threading; using System.Threading.Tasks; @@ -54,8 +55,9 @@ public GetPii( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Pii)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Pii)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); diff --git a/polaris-gateway/Functions/GetThumbnail.cs b/polaris-gateway/Functions/GetThumbnail.cs index db96662d8..3175b2ee1 100644 --- a/polaris-gateway/Functions/GetThumbnail.cs +++ b/polaris-gateway/Functions/GetThumbnail.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -53,7 +54,7 @@ public GetThumbnail( public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.Thumbnail)] HttpRequest req, - string caseUrn, int caseId, string materialId, int documentId, int maxDimensionPixel, int pageIndex) + string caseUrn, int caseId, string materialId, int documentId, int maxDimensionPixel, int pageIndex, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); diff --git a/polaris-gateway/Functions/GetWitnessStatements.cs b/polaris-gateway/Functions/GetWitnessStatements.cs index 10f3ef8e9..ca77c5384 100644 --- a/polaris-gateway/Functions/GetWitnessStatements.cs +++ b/polaris-gateway/Functions/GetWitnessStatements.cs @@ -12,6 +12,7 @@ using Microsoft.OpenApi.Models; using System.Collections.Generic; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -42,13 +43,13 @@ public GetWitnessStatements( [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(IEnumerable), Summary = "Case found", Description = "Returns case details")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.WitnessStatements)] HttpRequest req, string caseUrn, int caseId, int witnessId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.WitnessStatements)] HttpRequest req, string caseUrn, int caseId, int witnessId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); var witnessStatementsArgDto = _mdsArgFactory.CreateWitnessStatementsArgDto(cmsAuthValues, correlationId, caseUrn, caseId, witnessId); - var witnessStatementDtos = await _mdsClient.GetWitnessStatementsAsync(witnessStatementsArgDto); + var witnessStatementDtos = await _mdsClient.GetWitnessStatementsAsync(witnessStatementsArgDto, cancellationToken: cancellationToken); return new OkObjectResult(witnessStatementDtos); } diff --git a/polaris-gateway/Functions/GetWitnesses.cs b/polaris-gateway/Functions/GetWitnesses.cs index 307f62bb1..8b237327d 100644 --- a/polaris-gateway/Functions/GetWitnesses.cs +++ b/polaris-gateway/Functions/GetWitnesses.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -46,14 +47,14 @@ public GetWitnesses( [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(IEnumerable), Summary = "Case found", Description = "Returns case details")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseWitnesses)] HttpRequest req, string caseUrn, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseWitnesses)] HttpRequest req, string caseUrn, int caseId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); var arg = _mdsArgFactory.CreateCaseIdentifiersArg(cmsAuthValues, correlationId, caseUrn, caseId); - var caseWitnessResponses = await _mdsClient.GetWitnessesAsync(arg); + var caseWitnessResponses = await _mdsClient.GetWitnessesAsync(arg, cancellationToken: cancellationToken); var caseWitnesses = caseWitnessResponses.Select(_caseWitnessMapper.Map); return new OkObjectResult(caseWitnesses); diff --git a/polaris-gateway/Functions/HouseKeeping/BulkSetUnused.cs b/polaris-gateway/Functions/HouseKeeping/BulkSetUnused.cs index fdfaabe59..4630beda8 100644 --- a/polaris-gateway/Functions/HouseKeeping/BulkSetUnused.cs +++ b/polaris-gateway/Functions/HouseKeeping/BulkSetUnused.cs @@ -10,6 +10,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using System.IO; using System.Net; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; using Azure.Core; using Common.Configuration; @@ -53,7 +54,7 @@ public class BulkSetUnused(ILogger logger, IBulkSetUnusedService [OpenApiRequestBody("application/json", typeof(BulkSetUnusedResponse), Description = "Return success response.")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.OK)] [Function("BulkSetUnused")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.BulkSetUnused)] HttpRequest req, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.BulkSetUnused)] HttpRequest req, int caseId, CancellationToken cancellationToken = default) { try { @@ -83,7 +84,7 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, return new BadRequestObjectResult($"{LoggingConstants.HskUiLogPrefix} Invalid or empty request body."); } - BulkSetUnusedResponse result = await this.bulkSetUnusedService.BulkSetUnusedAsync(caseId, cmsAuthValues, bulkSetUnusedRequests).ConfigureAwait(true); + BulkSetUnusedResponse result = await this.bulkSetUnusedService.BulkSetUnusedAsync(caseId, cmsAuthValues, bulkSetUnusedRequests, cancellationToken).ConfigureAwait(true); if (result.Status?.Equals("failed") == true) { diff --git a/polaris-gateway/Functions/HouseKeeping/CompleteReclassification.cs b/polaris-gateway/Functions/HouseKeeping/CompleteReclassification.cs index 089d1a951..ad39325bf 100644 --- a/polaris-gateway/Functions/HouseKeeping/CompleteReclassification.cs +++ b/polaris-gateway/Functions/HouseKeeping/CompleteReclassification.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; @@ -63,7 +64,7 @@ public class CompleteReclassification( [Function("CompleteReclassification")] public async Task Run( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.CompleteReclassification)] - HttpRequest request, int caseId, int materialId) + HttpRequest request, int caseId, int materialId, CancellationToken cancellationToken = default) { try { @@ -102,7 +103,8 @@ public async Task Run( caseId, materialId, cmsAuthValues, - completeReclassificationRequest).ConfigureAwait(false); + completeReclassificationRequest, + cancellationToken).ConfigureAwait(false); // All operations succeeded. if (result?.overallSuccess == true) diff --git a/polaris-gateway/Functions/HouseKeeping/DiscardMaterial.cs b/polaris-gateway/Functions/HouseKeeping/DiscardMaterial.cs index 3b7df567a..90b5aabeb 100644 --- a/polaris-gateway/Functions/HouseKeeping/DiscardMaterial.cs +++ b/polaris-gateway/Functions/HouseKeeping/DiscardMaterial.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -53,7 +54,7 @@ public class DiscardMaterial( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.UnprocessableEntity)] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.Unauthorized)] [Function("DiscardMaterial")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = RestApi.DiscardMaterial)] HttpRequest request, int caseId, int materialId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = RestApi.DiscardMaterial)] HttpRequest request, int caseId, int materialId, CancellationToken cancellationToken = default) { try { @@ -91,7 +92,8 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, discardMaterialRequest.materialId, discardMaterialRequest.discardReason, discardMaterialRequest.discardReasonDescription, - cmsAuthValues).ConfigureAwait(true); + cmsAuthValues, + cancellationToken: cancellationToken).ConfigureAwait(true); if (result?.DiscardMaterialData?.Id == null) { diff --git a/polaris-gateway/Functions/HouseKeeping/GetCaseDefendants.cs b/polaris-gateway/Functions/HouseKeeping/GetCaseDefendants.cs index dc8617b0a..b63f9a241 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetCaseDefendants.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetCaseDefendants.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -51,7 +52,7 @@ public class GetCaseDefendants( [OpenApiRequestBody("application/json", typeof(Defendant), Description = "Return case defendants response.")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.OK)] [Function("GetCaseDefendants")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseDefendants)] HttpRequest request, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseDefendants)] HttpRequest request, int caseId, CancellationToken cancellationToken = default) { try { @@ -69,7 +70,7 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, DefendantsResponse? caseDefendants; try { - caseDefendants = await this.caseDefendantsService.GetCaseDefendantsAsync(caseId, cmsAuthValues).ConfigureAwait(true); + caseDefendants = await this.caseDefendantsService.GetCaseDefendantsAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(true); this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Milestone: caseId [{caseId}] GetCaseDefendants function completed in [{stopwatch.Elapsed}]"); var response = new OkObjectResult(caseDefendants); diff --git a/polaris-gateway/Functions/HouseKeeping/GetCaseExhibitProducers.cs b/polaris-gateway/Functions/HouseKeeping/GetCaseExhibitProducers.cs index 8d7d3598b..77a9fda3a 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetCaseExhibitProducers.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetCaseExhibitProducers.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using System.Diagnostics; using System.Linq; using System.Net; +using System.Threading; using System.Threading.Tasks; using Common.Configuration; using Common.Constants; @@ -51,7 +52,7 @@ public class GetCaseExhibitProducers(ILogger logger, [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.UnprocessableEntity)] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.Unauthorized)] [Function(nameof(GetCaseExhibitProducers))] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.ExhibitProducers)] HttpRequest request, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.ExhibitProducers)] HttpRequest request, int caseId, CancellationToken cancellationToken = default) { try { @@ -66,8 +67,8 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, // Build CMS auth values from cookie extracted from the request var cmsAuthValues = this.BuildCmsAuthValues(request); - var exhibitTask = this.communicationService.GetExhibitProducersAsync(caseId, cmsAuthValues); - var witnessTask = this.witnessService.GetCaseWitnessesAsync(caseId, cmsAuthValues); + var exhibitTask = this.communicationService.GetExhibitProducersAsync(caseId, cmsAuthValues, cancellationToken); + var witnessTask = this.witnessService.GetCaseWitnessesAsync(caseId, cmsAuthValues, cancellationToken); await Task.WhenAll(exhibitTask, witnessTask).ConfigureAwait(false); diff --git a/polaris-gateway/Functions/HouseKeeping/GetCaseHistoryEvent.cs b/polaris-gateway/Functions/HouseKeeping/GetCaseHistoryEvent.cs index 046164c43..8ea12e808 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetCaseHistoryEvent.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetCaseHistoryEvent.cs @@ -7,6 +7,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using System; using System.Diagnostics; using System.Net; +using System.Threading; using System.Threading.Tasks; using Common.Configuration; using Common.Constants; @@ -53,7 +54,7 @@ public class GetCaseHistoryEvent( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.InternalServerError)] [Function(nameof(GetCaseHistoryEvent))] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseHistoryEvent)] HttpRequest request, int caseId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseHistoryEvent)] HttpRequest request, int caseId, CancellationToken cancellationToken = default) { try { @@ -68,7 +69,7 @@ public async Task Run( // Build CMS auth values from cookie extracted from the request var cmsAuthValues = this.BuildCmsAuthValues(request); - var result = await this.communicationService.GetHistoryEventsAsync(caseId, cmsAuthValues).ConfigureAwait(true); + var result = await this.communicationService.GetHistoryEventsAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(true); if (result == null) { diff --git a/polaris-gateway/Functions/HouseKeeping/GetCaseInfo.cs b/polaris-gateway/Functions/HouseKeeping/GetCaseInfo.cs index fb3b0722b..893f4b3c5 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetCaseInfo.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetCaseInfo.cs @@ -7,6 +7,7 @@ namespace PolarisGateway.Functions.HouseKeeping using System; using System.Diagnostics; using System.Net; + using System.Threading; using System.Threading.Tasks; using Common.Configuration; using Common.Constants; @@ -41,6 +42,7 @@ public class GetCaseInfo(ILogger logger, ICaseInfoService caseInfoS /// /// The HTTP request. /// The case Id. + /// The cancellation token to cancel the operation. /// An representing the response of the function. [OpenApiOperation(operationId: "GetCaseInfo", tags: ["Case"], Description = "Represents a function that retrieves the case information for display purposes.")] [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "x-functions-key", In = OpenApiSecurityLocationType.Header, Description = "The Azure Function API Key.")] @@ -48,32 +50,25 @@ public class GetCaseInfo(ILogger logger, ICaseInfoService caseInfoS [OpenApiRequestBody("application/json", typeof(CaseSummaryResponse), Description = "Return case summary response.")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.OK)] [Function("GetCaseInfo")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseInfo)] HttpRequest req, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseInfo)] HttpRequest req, int caseId, CancellationToken cancellationToken = default) { try { var stopwatch = Stopwatch.StartNew(); - this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} GetCaseInfo function processed a request."); - + this.logger.LogInformation("{Prefix} GetCaseInfo function processed a request.", LoggingConstants.HskUiLogPrefix); + if (caseId < 1) { return new BadRequestObjectResult($"{LoggingConstants.HskUiLogPrefix} Invalid case Id. It should be an integer."); } - + // Build CMS auth values from cookie extracted from the request var cmsAuthValues = this.BuildCmsAuthValues(req); CaseSummaryResponse caseSummary; - try - { - caseSummary = await this.caseInfoService.GetCaseInfoAsync(caseId, cmsAuthValues).ConfigureAwait(true); - this.logger.LogInformation(LoggingConstants.UnitNameExtractionSuccess, LoggingConstants.HskUiLogPrefix, caseSummary.UnitName, caseId); - } - catch (Exception ex) - { - this.logger.LogError($"{LoggingConstants.HskUiLogPrefix} GetCaseInfo function encountered an error fetching case information for caseId [{caseId}]: {ex.Message}"); - throw new UnprocessableEntityException($"GetCaseInfo function encountered an error fetching case information for caseId [{caseId}]"); - } + + caseSummary = await this.caseInfoService.GetCaseInfoAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); + this.logger.LogInformation(LoggingConstants.UnitNameExtractionSuccess, LoggingConstants.HskUiLogPrefix, caseSummary.UnitName, caseId); var response = new OkObjectResult(caseSummary); @@ -83,9 +78,15 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, return response; } + catch (OperationCanceledException) + { + // Let cancellation exceptions propagate naturally - they should not be converted to HTTP errors + // The runtime will handle them appropriately, no logging needed as this is a control-flow exception + throw; + } catch (UnprocessableEntityException ex) { - this.logger.LogError($"{LoggingConstants.HskUiLogPrefix} GetCaseInfo function encountered an unprocessable entity error: {ex.Message}"); + this.logger.LogError(ex, "{Prefix} GetCaseInfo function encountered an unprocessable entity error: {Message}", LoggingConstants.HskUiLogPrefix, ex.Message); return new ObjectResult(ex.Message) { StatusCode = StatusCodes.Status422UnprocessableEntity, @@ -93,12 +94,12 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, } catch (InvalidOperationException ex) { - this.logger.LogError($"{LoggingConstants.HskUiLogPrefix} {ex.Message}"); + this.logger.LogError(ex, "{Prefix} {Message}", LoggingConstants.HskUiLogPrefix, ex.Message); return new UnprocessableEntityObjectResult($"{ex.Message}"); } catch (Exception ex) { - this.logger.LogError($"{LoggingConstants.HskUiLogPrefix} GetCaseInfo function encountered an error: {ex.Message}"); + this.logger.LogError(ex, "{Prefix} GetCaseInfo function encountered an error: {Message}", LoggingConstants.HskUiLogPrefix, ex.Message); return new StatusCodeResult(StatusCodes.Status500InternalServerError); } } diff --git a/polaris-gateway/Functions/HouseKeeping/GetCaseLockInfo.cs b/polaris-gateway/Functions/HouseKeeping/GetCaseLockInfo.cs index ff0160774..278d58524 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetCaseLockInfo.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetCaseLockInfo.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -50,7 +51,7 @@ public class GetCaseLockInfo(ILogger logger, ICaseLockService c [OpenApiRequestBody("application/json", typeof(CaseLockedStatusResult), Description = "Return case lock response.")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.OK)] [Function(nameof(GetCaseLockInfo))] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseLockInfo)] HttpRequest req, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseLockInfo)] HttpRequest req, int caseId, CancellationToken cancellationToken = default) { try { @@ -68,7 +69,7 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, CaseLockedStatusResult caseLockSummary; try { - caseLockSummary = await this.caseLockService.CheckCaseLockAsync(caseId, cmsAuthValues).ConfigureAwait(true); + caseLockSummary = await this.caseLockService.CheckCaseLockAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(true); } catch (Exception ex) { diff --git a/polaris-gateway/Functions/HouseKeeping/GetCaseMaterials.cs b/polaris-gateway/Functions/HouseKeeping/GetCaseMaterials.cs index 391eaf675..e07968c8c 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetCaseMaterials.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetCaseMaterials.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -27,6 +28,26 @@ namespace PolarisGateway.Functions.HouseKeeping; using Common.Configuration; using Common.Constants; using Common.Enums; +using Cps.Fct.Hk.Ui.Services.Constants; + +/// +/// Container for retrieved case materials data. +/// +/// The communications collection. +/// The unused materials. +/// The used statements. +/// The used exhibits. +/// The used MG forms. +/// The used other materials. +/// The exhibit producers. +internal record RetrievedCaseMaterials( + IReadOnlyCollection Communications, + UnusedMaterialsResponse UnusedMaterials, + UsedStatementsResponse UsedStatements, + UsedExhibitsResponse UsedExhibits, + UsedMgFormsResponse UsedMgForms, + UsedOtherMaterialsResponse UsedOtherMaterials, + ExhibitProducersResponse ExhibitProducers); /// /// Represents a function that retrieves the case materials for a case, @@ -42,7 +63,7 @@ namespace PolarisGateway.Functions.HouseKeeping; public class GetCaseMaterials( ILogger logger, ICommunicationService communicationService, - ICaseMaterialService caseMaterialService): BaseFunction(logger) + ICaseMaterialService caseMaterialService) : BaseFunction(logger) { private readonly ILogger logger = logger; private readonly ICommunicationService communicationService = communicationService; @@ -53,6 +74,7 @@ public class GetCaseMaterials( /// /// The HTTP request. /// The case Id. + /// Cancellation token. /// An representing the response of the function. [OpenApiOperation(operationId: nameof(GetCaseMaterials), tags: ["Material"], Description = "Represents a function that retrieves the case materials for a case.")] [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "x-functions-key", In = OpenApiSecurityLocationType.Header, Description = "The Azure Function API Key.")] @@ -62,106 +84,38 @@ public class GetCaseMaterials( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.BadRequest)] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.UnprocessableEntity)] [Function("GetCaseMaterials")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseMaterials)] HttpRequest req, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseMaterials)] HttpRequest req, int caseId, CancellationToken cancellationToken = default) { try { var stopwatch = Stopwatch.StartNew(); this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} GetCaseMaterials function processed a request."); - // Build CMS auth values from cookie extracted from the request var cmsAuthValues = this.BuildCmsAuthValues(req); - - // Retrieve case materials var (communications, unusedMaterials, usedStatements, usedExhibits, usedMgForms, usedOtherMaterials, exhibitProducers) = - await this.caseMaterialService.RetrieveCaseMaterialsAsync(caseId, cmsAuthValues).ConfigureAwait(false); - - // Ensure all required data is retrieved before proceeding - if (communications == null || unusedMaterials == null || usedStatements == null || usedExhibits == null || usedMgForms == null || usedOtherMaterials == null) - { - this.logger.LogError($"{LoggingConstants.HskUiLogPrefix} Failed to retrieve case materials for caseId [{caseId}]"); - throw new UnprocessableEntityException($"Failed to retrieve case materials for caseId [{caseId}]"); - } - - this.logger?.LogInformation( - $"{LoggingConstants.HskUiLogPrefix} caseId [{caseId}] material count: " + - $"communications [{communications.Count}], " + - $"unusedMaterials (exhibits) [{unusedMaterials.Exhibits?.Count ?? 0}], " + - $"unusedMaterials (mgForms) [{unusedMaterials.MgForms?.Count ?? 0}], " + - $"unusedMaterials (otherMaterials) [{unusedMaterials.OtherMaterials?.Count ?? 0}], " + - $"unusedMaterials (statements) [{unusedMaterials.Statements?.Count ?? 0}], " + - $"usedStatements [{usedStatements.Statements?.Count ?? 0}], " + - $"usedExhibits [{usedExhibits.Exhibits?.Count ?? 0}], " + - $"usedMgForms [{usedMgForms.MgForms?.Count ?? 0}], " + - $"usedOtherMaterials [{usedOtherMaterials.MgForms?.Count ?? 0}] " + - $"exhibitProducers [{exhibitProducers.ExhibitProducers?.Count ?? 0}]"); - - // Map and combine communications with attachments - List combinedCommunications = - await this.GetMappedCommunicationsWithAttachmentsAsync(caseId, communications, cmsAuthValues).ConfigureAwait(false); - - // Map communications to CaseMaterials - List allCaseMaterials = this.caseMaterialService.MapCommunicationsToCaseMaterials(combinedCommunications) ?? new List(); - - // Add Used exhibits - this.caseMaterialService.AddCaseMaterials(allCaseMaterials!, usedExhibits.Exhibits ?? Enumerable.Empty(), "Exhibit", "Other Exhibit", "Used"); - if (usedExhibits.Exhibits != null && usedExhibits.Exhibits.Count != 0) - { - allCaseMaterials?.AddRange(this.caseMaterialService.MapUsedExhibitsToCaseMaterials(usedExhibits, exhibitProducers, communications, caseId)); - } - - // Add Used statements - this.caseMaterialService.AddCaseMaterials(allCaseMaterials!, usedStatements.Statements ?? Enumerable.Empty(), "Statement", "Other Statement", "Used"); - if (usedStatements.Statements != null && usedStatements.Statements.Count != 0) - { - allCaseMaterials?.AddRange(this.caseMaterialService.MapUsedStatementsToCaseMaterials(usedStatements, communications)); - } + await this.caseMaterialService.RetrieveCaseMaterialsAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); - // Add Used MG forms - this.caseMaterialService.AddCaseMaterials(allCaseMaterials!, usedMgForms.MgForms ?? Enumerable.Empty(), "MG Form", "MG Form", "Used"); - if (usedMgForms.MgForms != null && usedMgForms.MgForms.Count != 0) - { - allCaseMaterials?.AddRange(this.caseMaterialService.MapUsedMgFormsToCaseMaterials(usedMgForms)); - } + var retrievedMaterials = new RetrievedCaseMaterials( + communications, + unusedMaterials, + usedStatements, + usedExhibits, + usedMgForms, + usedOtherMaterials, + exhibitProducers); - // Add Used other materials - this.caseMaterialService.AddCaseMaterials(allCaseMaterials!, usedOtherMaterials.MgForms ?? Enumerable.Empty(), "Other Material", "Other Material", "Used"); - if (usedOtherMaterials.MgForms != null && usedOtherMaterials.MgForms.Count != 0) - { - allCaseMaterials?.AddRange(this.caseMaterialService.MapUsedOtherMaterialsToCaseMaterials(usedOtherMaterials)); - } + this.ValidateRetrievedMaterials(caseId, retrievedMaterials); + this.LogMaterialCounts(caseId, retrievedMaterials); - // Add Unused Materials - foreach (var collection in new List<(string Name, IEnumerable UnusedMaterials)> - { - ("Exhibits", unusedMaterials.Exhibits ?? Enumerable.Empty()), - ("MgForms", unusedMaterials.MgForms ?? Enumerable.Empty()), - ("OtherMaterials", unusedMaterials.OtherMaterials ?? Enumerable.Empty()), - ("Statements", unusedMaterials.Statements ?? Enumerable.Empty()), - }) - { - if (collection.UnusedMaterials.Any()) - { - this.caseMaterialService.AddCaseMaterials(allCaseMaterials!, collection.UnusedMaterials, collection.Name, "Unused Material", "Unused"); - } - } + var combinedCommunications = await this.GetMappedCommunicationsWithAttachmentsAsync(caseId, communications, cmsAuthValues).ConfigureAwait(false); + var allCaseMaterials = this.caseMaterialService.MapCommunicationsToCaseMaterials(combinedCommunications) ?? new List(); - // Map and add unused materials only if the result is not empty - List mappedUnusedMaterials = this.caseMaterialService.MapUnusedMaterialsToCaseMaterials(unusedMaterials, communications); - if (mappedUnusedMaterials != null && mappedUnusedMaterials.Any()) - { - allCaseMaterials?.AddRange(mappedUnusedMaterials); - } + this.ProcessUsedMaterials(allCaseMaterials, caseId, retrievedMaterials); + this.ProcessUnusedMaterials(allCaseMaterials, communications, unusedMaterials); this.logger!.LogInformation($"{LoggingConstants.HskUiLogPrefix} Milestone: caseId [{caseId}] GetCaseMaterials function completed in [{stopwatch.Elapsed}]"); - if (allCaseMaterials != null) - { - foreach (CaseMaterial allCaseMaterial in allCaseMaterials) - { - allCaseMaterial.ReadStatus = allCaseMaterial.ReadStatus.Equals("Complete", StringComparison.OrdinalIgnoreCase) ? MaterialReadStatusType.Read.ToString() : MaterialReadStatusType.Unread.ToString(); - } - } + NormalizeReadStatus(allCaseMaterials); var response = new OkObjectResult(allCaseMaterials); ConfigureResponseHeaders(req.HttpContext.Response); @@ -182,6 +136,162 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, } } + /// + /// Validates that all required case materials were retrieved successfully. + /// + private void ValidateRetrievedMaterials(int caseId, RetrievedCaseMaterials materials) + { + if (materials.Communications == null || + materials.UnusedMaterials == null || + materials.UsedStatements == null || + materials.UsedExhibits == null || + materials.UsedMgForms == null || + materials.UsedOtherMaterials == null) + { + this.logger.LogError($"{LoggingConstants.HskUiLogPrefix} Failed to retrieve case materials for caseId [{caseId}]"); + throw new UnprocessableEntityException($"Failed to retrieve case materials for caseId [{caseId}]"); + } + } + + /// + /// Logs the counts of all retrieved materials. + /// + private void LogMaterialCounts(int caseId, RetrievedCaseMaterials materials) + { + this.logger?.LogInformation( + $"{LoggingConstants.HskUiLogPrefix} caseId [{caseId}] material count: " + + $"communications [{materials.Communications.Count}], " + + $"unusedMaterials (exhibits) [{materials.UnusedMaterials.Exhibits?.Count ?? 0}], " + + $"unusedMaterials (mgForms) [{materials.UnusedMaterials.MgForms?.Count ?? 0}], " + + $"unusedMaterials (otherMaterials) [{materials.UnusedMaterials.OtherMaterials?.Count ?? 0}], " + + $"unusedMaterials (statements) [{materials.UnusedMaterials.Statements?.Count ?? 0}], " + + $"usedStatements [{materials.UsedStatements.Statements?.Count ?? 0}], " + + $"usedExhibits [{materials.UsedExhibits.Exhibits?.Count ?? 0}], " + + $"usedMgForms [{materials.UsedMgForms.MgForms?.Count ?? 0}], " + + $"usedOtherMaterials [{materials.UsedOtherMaterials.MgForms?.Count ?? 0}] " + + $"exhibitProducers [{materials.ExhibitProducers.ExhibitProducers?.Count ?? 0}]"); + } + + /// + /// Processes and adds all used materials to the case materials list. + /// + private void ProcessUsedMaterials( + List allCaseMaterials, + int caseId, + RetrievedCaseMaterials materials) + { + this.AddUsedExhibits(allCaseMaterials, caseId, materials.Communications, materials.UsedExhibits, materials.ExhibitProducers); + this.AddUsedStatements(allCaseMaterials, materials.Communications, materials.UsedStatements); + this.AddUsedMgForms(allCaseMaterials, materials.UsedMgForms); + this.AddUsedOtherMaterials(allCaseMaterials, materials.UsedOtherMaterials); + } + + /// + /// Adds used exhibits to the case materials list. + /// + private void AddUsedExhibits( + List allCaseMaterials, + int caseId, + IReadOnlyCollection communications, + UsedExhibitsResponse usedExhibits, + ExhibitProducersResponse exhibitProducers) + { + this.caseMaterialService.AddCaseMaterials(allCaseMaterials, usedExhibits.Exhibits ?? Enumerable.Empty(), "Exhibit", "Other Exhibit", "Used"); + if (usedExhibits.Exhibits != null && usedExhibits.Exhibits.Count != 0) + { + allCaseMaterials.AddRange(this.caseMaterialService.MapUsedExhibitsToCaseMaterials(usedExhibits, exhibitProducers, communications, caseId)); + } + } + + /// + /// Adds used statements to the case materials list. + /// + private void AddUsedStatements( + List allCaseMaterials, + IReadOnlyCollection communications, + UsedStatementsResponse usedStatements) + { + this.caseMaterialService.AddCaseMaterials(allCaseMaterials, usedStatements.Statements ?? Enumerable.Empty(), "Statement", "Other Statement", "Used"); + if (usedStatements.Statements != null && usedStatements.Statements.Count != 0) + { + allCaseMaterials.AddRange(this.caseMaterialService.MapUsedStatementsToCaseMaterials(usedStatements, communications)); + } + } + + /// + /// Adds used MG forms to the case materials list. + /// + private void AddUsedMgForms(List allCaseMaterials, UsedMgFormsResponse usedMgForms) + { + if (usedMgForms.MgForms != null) + { + usedMgForms.MgForms.RemoveAll(mgForm => CommsDocumentTypeIds.ExcludedFromUsedMgForms.Contains(mgForm.MaterialType)); + } + + this.caseMaterialService.AddCaseMaterials(allCaseMaterials, usedMgForms.MgForms ?? Enumerable.Empty(), "MG Form", "MG Form", "Used"); + if (usedMgForms.MgForms != null && usedMgForms.MgForms.Count != 0) + { + allCaseMaterials.AddRange(this.caseMaterialService.MapUsedMgFormsToCaseMaterials(usedMgForms)); + } + } + + /// + /// Adds used other materials to the case materials list. + /// + private void AddUsedOtherMaterials(List allCaseMaterials, UsedOtherMaterialsResponse usedOtherMaterials) + { + this.caseMaterialService.AddCaseMaterials(allCaseMaterials, usedOtherMaterials.MgForms ?? Enumerable.Empty(), "Other Material", "Other Material", "Used"); + if (usedOtherMaterials.MgForms != null && usedOtherMaterials.MgForms.Count != 0) + { + allCaseMaterials.AddRange(this.caseMaterialService.MapUsedOtherMaterialsToCaseMaterials(usedOtherMaterials)); + } + } + + /// + /// Processes and adds all unused materials to the case materials list. + /// + private void ProcessUnusedMaterials( + List allCaseMaterials, + IReadOnlyCollection communications, + UnusedMaterialsResponse unusedMaterials) + { + foreach (var collection in new List<(string Name, IEnumerable UnusedMaterials)> + { + ("Exhibits", unusedMaterials.Exhibits ?? Enumerable.Empty()), + ("MgForms", unusedMaterials.MgForms ?? Enumerable.Empty()), + ("OtherMaterials", unusedMaterials.OtherMaterials ?? Enumerable.Empty()), + ("Statements", unusedMaterials.Statements ?? Enumerable.Empty()), + }) + { + if (collection.UnusedMaterials.Any()) + { + this.caseMaterialService.AddCaseMaterials(allCaseMaterials, collection.UnusedMaterials, collection.Name, "Unused Material", "Unused"); + } + } + + var mappedUnusedMaterials = this.caseMaterialService.MapUnusedMaterialsToCaseMaterials(unusedMaterials, communications); + if (mappedUnusedMaterials != null && mappedUnusedMaterials.Any()) + { + allCaseMaterials.AddRange(mappedUnusedMaterials); + } + } + + /// + /// Normalizes the read status of all case materials. + /// + private static void NormalizeReadStatus(List allCaseMaterials) + { + if (allCaseMaterials != null) + { + foreach (var caseMaterial in allCaseMaterials) + { + caseMaterial.ReadStatus = caseMaterial.ReadStatus.Equals("Complete", StringComparison.OrdinalIgnoreCase) + ? MaterialReadStatusType.Read.ToString() + : MaterialReadStatusType.Unread.ToString(); + } + } + } + /// /// Configures the response headers to enhance security and prevent caching. /// diff --git a/polaris-gateway/Functions/HouseKeeping/GetCaseMaterialsPreview.cs b/polaris-gateway/Functions/HouseKeeping/GetCaseMaterialsPreview.cs index 7c6eed3e1..278e5902e 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetCaseMaterialsPreview.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetCaseMaterialsPreview.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -57,7 +58,7 @@ public class GetCaseMaterialsPreview( [OpenApiRequestBody("application/json", typeof(FileStreamResult), Description = "Return case summary response.")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.OK)] [Function("GetCaseMaterialsPreview")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseMaterialsPreview)] HttpRequest req, int caseId, int materialId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseMaterialsPreview)] HttpRequest req, int caseId, int materialId, CancellationToken cancellationToken = default) { try { @@ -78,7 +79,7 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, var cmsAuthValues = this.BuildCmsAuthValues(req); // Get the material link - string link = await this.GetLinkForMaterialAsync(caseId, materialId, cmsAuthValues).ConfigureAwait(true); + string link = await this.GetLinkForMaterialAsync(caseId, materialId, cmsAuthValues, cancellationToken).ConfigureAwait(true); if (string.IsNullOrEmpty(link)) { return new NotFoundObjectResult($"{LoggingConstants.HskUiLogPrefix} No valid link found for the case material document with materialId [{materialId}]."); @@ -125,9 +126,9 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, /// /// A containing the communication link if found; otherwise, returns null. /// - private async Task GetLinkForMaterialAsync(int caseId, int materialId, CmsAuthValues cmsAuthValues) + private async Task GetLinkForMaterialAsync(int caseId, int materialId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { - object linkResult = await this.communicationService.GetCaseMaterialLinkAsync(caseId, materialId, cmsAuthValues).ConfigureAwait(true); + object linkResult = await this.communicationService.GetCaseMaterialLinkAsync(caseId, materialId, cmsAuthValues, cancellationToken).ConfigureAwait(true); return linkResult is IActionResult ? null : linkResult as string; } diff --git a/polaris-gateway/Functions/HouseKeeping/GetCaseWitnessStatements.cs b/polaris-gateway/Functions/HouseKeeping/GetCaseWitnessStatements.cs index d702ee7a3..632be275a 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetCaseWitnessStatements.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetCaseWitnessStatements.cs @@ -7,6 +7,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using System; using System.Diagnostics; using System.Net; +using System.Threading; using System.Threading.Tasks; using Common.Configuration; using Common.Constants; @@ -50,7 +51,7 @@ public class GetCaseWitnessStatements( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.UnprocessableEntity)] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.Unauthorized)] [Function(nameof(GetCaseWitnessStatements))] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseWitnessStatements)] HttpRequest request, int caseId, int witnessId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseWitnessStatements)] HttpRequest request, int caseId, int witnessId, CancellationToken cancellationToken = default) { try { @@ -65,7 +66,7 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, // Build CMS auth values from cookie extracted from the request var cmsAuthValues = this.BuildCmsAuthValues(request); - WitnessStatementsResponse? result = await this.witnessService.GetWitnessStatementsAsync(witnessId, cmsAuthValues).ConfigureAwait(false); + WitnessStatementsResponse? result = await this.witnessService.GetWitnessStatementsAsync(witnessId, cmsAuthValues, cancellationToken).ConfigureAwait(false); this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Milestone: caseId [{caseId}] witnessId [{witnessId}] {nameof(GetCaseWitnessStatements)} function completed in [{stopwatch.Elapsed}]"); diff --git a/polaris-gateway/Functions/HouseKeeping/GetCaseWitnesses.cs b/polaris-gateway/Functions/HouseKeeping/GetCaseWitnesses.cs index 86e0a63ac..adf9cc1c5 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetCaseWitnesses.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetCaseWitnesses.cs @@ -7,6 +7,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using System; using System.Diagnostics; using System.Net; +using System.Threading; using System.Threading.Tasks; using Common.Configuration; using Common.Constants; @@ -48,7 +49,7 @@ public class GetCaseWitnesses(ILogger logger, [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.UnprocessableEntity)] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.Unauthorized)] [Function(nameof(GetCaseWitnesses))] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseWitnessesHk)] HttpRequest request, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseWitnessesHk)] HttpRequest request, int caseId, CancellationToken cancellationToken = default) { try { @@ -63,7 +64,7 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, // Build CMS auth values from cookie extracted from the request var cmsAuthValues = BuildCmsAuthValues(request); - WitnessesResponse result = await witnessService.GetCaseWitnessesAsync(caseId, cmsAuthValues).ConfigureAwait(false); + WitnessesResponse result = await witnessService.GetCaseWitnessesAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Milestone: caseId [{caseId}] {nameof(GetCaseWitnesses)} function completed in [{stopwatch.Elapsed}]"); diff --git a/polaris-gateway/Functions/HouseKeeping/GetInitialReview.cs b/polaris-gateway/Functions/HouseKeeping/GetInitialReview.cs index 77a9ff4ef..5c73ed6fa 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetInitialReview.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetInitialReview.cs @@ -7,6 +7,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using System; using System.Diagnostics; using System.Net; +using System.Threading; using System.Threading.Tasks; using Common.Configuration; using Common.Constants; @@ -53,7 +54,7 @@ public class GetInitialReview( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.InternalServerError)] [Function(nameof(GetInitialReview))] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.InitialReviewByCase)] HttpRequest request, int caseId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.InitialReviewByCase)] HttpRequest request, int caseId, CancellationToken cancellationToken = default) { try { @@ -68,7 +69,7 @@ public async Task Run( // Build CMS auth values from cookie extracted from the request var cmsAuthValues = this.BuildCmsAuthValues(request); - var result = await this.caseHistoryEventProvider.FirstInitialReviewGetCaseHistoryAsync(caseId, cmsAuthValues).ConfigureAwait(true); + var result = await this.caseHistoryEventProvider.FirstInitialReviewGetCaseHistoryAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(true); if (result == null) { diff --git a/polaris-gateway/Functions/HouseKeeping/GetMaterialDocuments.cs b/polaris-gateway/Functions/HouseKeeping/GetMaterialDocuments.cs index fb8444d38..7238e0592 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetMaterialDocuments.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetMaterialDocuments.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -56,7 +57,7 @@ public class GetMaterialDocument( [OpenApiRequestBody("application/json", typeof(FileStreamResult), Description = "Return case summary response.")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.OK)] [Function("GetMaterialDocument")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.MaterialDocument)] HttpRequest req, int caseId, int materialId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.MaterialDocument)] HttpRequest req, int caseId, int materialId, CancellationToken cancellationToken = default) { try { @@ -125,9 +126,9 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, /// /// A containing the communication link if found; otherwise, returns null. /// - private async Task GetLinkForMaterialAsync(int caseId, int materialId, CmsAuthValues cmsAuthValues) + private async Task GetLinkForMaterialAsync(int caseId, int materialId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { - object linkResult = await this.communicationService.GetCaseMaterialLinkAsync(caseId, materialId, cmsAuthValues).ConfigureAwait(true); + object linkResult = await this.communicationService.GetCaseMaterialLinkAsync(caseId, materialId, cmsAuthValues, cancellationToken).ConfigureAwait(true); return linkResult is IActionResult ? null : linkResult as string; } @@ -142,9 +143,9 @@ private async Task GetLinkForMaterialAsync(int caseId, int materialId, C /// /// An that either contains the material document if found, or a file with 'not found' content if the document is missing. /// - private async Task GetMaterialDocumentAsync(string caseId, string link, CmsAuthValues cmsAuthValues, Stopwatch stopwatch) + private async Task GetMaterialDocumentAsync(string caseId, string link, CmsAuthValues cmsAuthValues, Stopwatch stopwatch, CancellationToken cancellationToken = default) { - FileStreamResult downloadedDocument = await this.documentService.GetMaterialDocumentAsync(caseId, link, cmsAuthValues, firstPageOnly: false).ConfigureAwait(true); + FileStreamResult downloadedDocument = await this.documentService.GetMaterialDocumentAsync(caseId, link, cmsAuthValues, firstPageOnly: false, cancellationToken).ConfigureAwait(true); if (downloadedDocument == null) { diff --git a/polaris-gateway/Functions/HouseKeeping/GetOffenceChargeById.cs b/polaris-gateway/Functions/HouseKeeping/GetOffenceChargeById.cs index 82e6c382c..5d1ef8289 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetOffenceChargeById.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetOffenceChargeById.cs @@ -1,71 +1,73 @@ -// -// Copyright (c) The Crown Prosecution Service. All rights reserved. -// - -namespace PolarisGateway.Functions.HouseKeeping; - -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Extensions.Logging; -using System.Threading.Tasks; -using Cps.Fct.Hk.Ui.Interfaces; -using System.Diagnostics; -using Microsoft.OpenApi.Models; -using System.Net; -using System; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; -using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; +// +// Copyright (c) The Crown Prosecution Service. All rights reserved. +// + +namespace PolarisGateway.Functions.HouseKeeping; + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.Logging; +using System.Threading; +using System.Threading.Tasks; +using Cps.Fct.Hk.Ui.Interfaces; +using System.Diagnostics; +using Microsoft.OpenApi.Models; +using System.Net; +using System; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes; +using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums; using Common.Configuration; using Common.Constants; using Common.Exceptions; -/// -/// Represents a function that return case offence charge by case id, -/// intended to be accessed via the Housekeeping UI front-end. -/// -/// -/// Initializes a new instance of the class. -/// -/// The logger instance used to log information and errors. -/// The service used to get call case history service. -/// The service used to handle cookie-related operations. -public class GetOffenceChargeById( - ILogger logger, - ICommunicationService communicationService) : BaseFunction(logger) -{ - private readonly ILogger logger = logger; - private readonly ICommunicationService communicationService = communicationService; - - /// - /// The Azure Function that processes an HTTP request for the 'case/{caseId}/pcd-review' route. - /// - /// The HTTP request. - /// The case id. - /// The history Id of the case to get offence charge. - /// An representing the response of the function. - [OpenApiOperation(operationId: nameof(GetOffenceChargeById), tags: ["CaseHistory"], Description = "Returns Offence charge with Case Id.")] - [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "x-functions-key", In = OpenApiSecurityLocationType.Header, Description = "The Azure Function API Key.")] - [OpenApiSecurity("Cookie", SecuritySchemeType.ApiKey, Name = "Cookie", In = OpenApiSecurityLocationType.Header, Description = "The CMS Auth Values. This can be retrieved via the DDEI Authenticate API Endpoint and URI encoded along with User session token.")] - [OpenApiParameter("caseId", In = ParameterLocation.Path, Type = typeof(int), Description = "The Id of the case.", Required = true)] - [OpenApiParameter("historyId", In = ParameterLocation.Path, Type = typeof(int), Description = "The history Id of the case to get offence charge.", Required = true)] - [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(Cps.MasterDataService.Infrastructure.ApiClient.PreChargeDecisionOutcome), Description = "Return PCD review.")] - [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.BadRequest)] - [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.UnprocessableEntity)] - [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.Unauthorized)] - [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.InternalServerError)] - [Function(nameof(GetOffenceChargeById))] - public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.OffenseCharge)] - HttpRequest request, - int caseId, - int historyId) - { - try - { - var stopwatch = Stopwatch.StartNew(); - this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} GetOffenceChargeById function processed a request."); - +/// +/// Represents a function that return case offence charge by case id, +/// intended to be accessed via the Housekeeping UI front-end. +/// +/// +/// Initializes a new instance of the class. +/// +/// The logger instance used to log information and errors. +/// The service used to get call case history service. +/// The service used to handle cookie-related operations. +public class GetOffenceChargeById( + ILogger logger, + ICommunicationService communicationService) : BaseFunction(logger) +{ + private readonly ILogger logger = logger; + private readonly ICommunicationService communicationService = communicationService; + + /// + /// The Azure Function that processes an HTTP request for the 'case/{caseId}/pcd-review' route. + /// + /// The HTTP request. + /// The case id. + /// The history Id of the case to get offence charge. + /// An representing the response of the function. + [OpenApiOperation(operationId: nameof(GetOffenceChargeById), tags: ["CaseHistory"], Description = "Returns Offence charge with Case Id.")] + [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "x-functions-key", In = OpenApiSecurityLocationType.Header, Description = "The Azure Function API Key.")] + [OpenApiSecurity("Cookie", SecuritySchemeType.ApiKey, Name = "Cookie", In = OpenApiSecurityLocationType.Header, Description = "The CMS Auth Values. This can be retrieved via the DDEI Authenticate API Endpoint and URI encoded along with User session token.")] + [OpenApiParameter("caseId", In = ParameterLocation.Path, Type = typeof(int), Description = "The Id of the case.", Required = true)] + [OpenApiParameter("historyId", In = ParameterLocation.Path, Type = typeof(int), Description = "The history Id of the case to get offence charge.", Required = true)] + [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(Cps.MasterDataService.Infrastructure.ApiClient.PreChargeDecisionOutcome), Description = "Return PCD review.")] + [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.BadRequest)] + [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.UnprocessableEntity)] + [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.Unauthorized)] + [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.InternalServerError)] + [Function(nameof(GetOffenceChargeById))] + public async Task Run( + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.OffenseCharge)] + HttpRequest request, + int caseId, + int historyId, + CancellationToken cancellationToken = default) + { + try + { + var stopwatch = Stopwatch.StartNew(); + this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} GetOffenceChargeById function processed a request."); + if (caseId < 1) { return new BadRequestObjectResult($"{LoggingConstants.HskUiLogPrefix} Invalid case Id. It should be an integer."); @@ -77,41 +79,41 @@ public async Task Run( } // Build CMS auth values from cookie extracted from the request - var cmsAuthValues = this.BuildCmsAuthValues(request); - - var result = await this.communicationService.GetOffenceChargeByHistoryIdAsync(caseId, historyId, cmsAuthValues).ConfigureAwait(true); - - if (result == null) - { - this.logger.LogError($"{LoggingConstants.HskUiLogPrefix} caseId [{caseId}] GetOffenceChargeById function failed in [{stopwatch.Elapsed}]"); - return new UnprocessableEntityObjectResult(result); - } - - this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Milestone: caseId [{caseId}] GetOffenceChargeById function completed in [{stopwatch.Elapsed}]"); - - var response = new OkObjectResult(result); - - return response; - } - catch (InvalidOperationException ex) - { - this.logger.LogError($"{ex.Message}"); - return new UnprocessableEntityObjectResult($"{ex.Message}"); - } - catch (NotSupportedException ex) - { - this.logger.LogError(ex, $"{LoggingConstants.HskUiLogPrefix} GetOffenceChargeById function encountered unsupported content type."); - return new UnprocessableEntityObjectResult($"GetOffenceChargeById error: {ex.Message}"); - } - catch (UnauthorizedAccessException ex) - { - this.logger.LogError(ex, $"{LoggingConstants.HskUiLogPrefix} GetOffenceChargeById function encountered UnauthorizedAccess Exception."); - return new UnauthorizedObjectResult($"GetOffenceChargeById error: {ex.Message}"); - } - catch (Exception ex) - { - this.logger.LogError($"{LoggingConstants.HskUiLogPrefix} GetOffenceChargeById function encountered an error: {ex.Message}"); - return new StatusCodeResult(StatusCodes.Status500InternalServerError); - } - } -} + var cmsAuthValues = this.BuildCmsAuthValues(request); + + var result = await this.communicationService.GetOffenceChargeByHistoryIdAsync(caseId, historyId, cmsAuthValues, cancellationToken).ConfigureAwait(true); + + if (result == null) + { + this.logger.LogError($"{LoggingConstants.HskUiLogPrefix} caseId [{caseId}] GetOffenceChargeById function failed in [{stopwatch.Elapsed}]"); + return new UnprocessableEntityObjectResult(result); + } + + this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Milestone: caseId [{caseId}] GetOffenceChargeById function completed in [{stopwatch.Elapsed}]"); + + var response = new OkObjectResult(result); + + return response; + } + catch (InvalidOperationException ex) + { + this.logger.LogError($"{ex.Message}"); + return new UnprocessableEntityObjectResult($"{ex.Message}"); + } + catch (NotSupportedException ex) + { + this.logger.LogError(ex, $"{LoggingConstants.HskUiLogPrefix} GetOffenceChargeById function encountered unsupported content type."); + return new UnprocessableEntityObjectResult($"GetOffenceChargeById error: {ex.Message}"); + } + catch (UnauthorizedAccessException ex) + { + this.logger.LogError(ex, $"{LoggingConstants.HskUiLogPrefix} GetOffenceChargeById function encountered UnauthorizedAccess Exception."); + return new UnauthorizedObjectResult($"GetOffenceChargeById error: {ex.Message}"); + } + catch (Exception ex) + { + this.logger.LogError($"{LoggingConstants.HskUiLogPrefix} GetOffenceChargeById function encountered an error: {ex.Message}"); + return new StatusCodeResult(StatusCodes.Status500InternalServerError); + } + } +} diff --git a/polaris-gateway/Functions/HouseKeeping/GetPCDRequestByPcdId.cs b/polaris-gateway/Functions/HouseKeeping/GetPCDRequestByPcdId.cs index 371f79ab0..c04a94c3a 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetPCDRequestByPcdId.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetPCDRequestByPcdId.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -58,7 +59,7 @@ public class GetPcdRequestByPcdId( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.InternalServerError)] [Function(nameof(GetPcdRequestByPcdId))] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.PcdRequest)] HttpRequest request, int caseId, int pcdId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.PcdRequest)] HttpRequest request, int caseId, int pcdId, CancellationToken cancellationToken = default) { try { @@ -70,7 +71,7 @@ public async Task Run( string requestBody = await new StreamReader(request.Body).ReadToEndAsync().ConfigureAwait(false); - PcdRequestDto? result = await this.communicationService.GetPcdRequestByPcdIdAsync(caseId, pcdId, cmsAuthValues).ConfigureAwait(true); + PcdRequestDto? result = await this.communicationService.GetPcdRequestByPcdIdAsync(caseId, pcdId, cmsAuthValues, cancellationToken).ConfigureAwait(true); if (result == null) { diff --git a/polaris-gateway/Functions/HouseKeeping/GetPCDRequestCore.cs b/polaris-gateway/Functions/HouseKeeping/GetPCDRequestCore.cs index 8f3950b7a..48d0ebc72 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetPCDRequestCore.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetPCDRequestCore.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -57,7 +58,7 @@ public class GetPcdRequestCore( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.InternalServerError)] [Function(nameof(GetPcdRequestCore))] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.PcdRequestCore)] HttpRequest request, int caseId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.PcdRequestCore)] HttpRequest request, int caseId, CancellationToken cancellationToken = default) { try { @@ -69,7 +70,7 @@ public async Task Run( string requestBody = await new StreamReader(request.Body).ReadToEndAsync().ConfigureAwait(false); - IReadOnlyCollection result = await this.communicationService.GetPcdRequestCore(caseId, cmsAuthValues).ConfigureAwait(true); + IReadOnlyCollection result = await this.communicationService.GetPcdRequestCore(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(true); if (result == null) { diff --git a/polaris-gateway/Functions/HouseKeeping/GetPcdReview.cs b/polaris-gateway/Functions/HouseKeeping/GetPcdReview.cs index 245f0f244..44d3d83bd 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetPcdReview.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetPcdReview.cs @@ -7,6 +7,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using System; using System.Diagnostics; using System.Net; +using System.Threading; using System.Threading.Tasks; using Common.Configuration; using Common.Constants; @@ -53,7 +54,7 @@ public class GetPcdReview( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.InternalServerError)] [Function(nameof(GetPcdReview))] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.PcdReview)] HttpRequest request, int caseId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.PcdReview)] HttpRequest request, int caseId, CancellationToken cancellationToken = default) { try { @@ -68,7 +69,7 @@ public async Task Run( // Build CMS auth values from cookie extracted from the request var cmsAuthValues = this.BuildCmsAuthValues(request); - var result = await this.communicationService.GetPcdReview(caseId, cmsAuthValues); + var result = await this.communicationService.GetPcdReview(caseId, cmsAuthValues, cancellationToken); if (result == null) { diff --git a/polaris-gateway/Functions/HouseKeeping/GetPreChargeDecision.cs b/polaris-gateway/Functions/HouseKeeping/GetPreChargeDecision.cs index a4e1783ae..ebde2b6a8 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetPreChargeDecision.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetPreChargeDecision.cs @@ -7,6 +7,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using System; using System.Diagnostics; using System.Net; +using System.Threading; using System.Threading.Tasks; using Common.Configuration; using Common.Constants; @@ -53,7 +54,7 @@ public class GetPreChargeDecision( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.InternalServerError)] [Function(nameof(GetPreChargeDecision))] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.PreChargeDecision)] HttpRequest request, int caseId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.PreChargeDecision)] HttpRequest request, int caseId, CancellationToken cancellationToken = default) { try { @@ -68,7 +69,7 @@ public async Task Run( // Build CMS auth values from cookie extracted from the request var cmsAuthValues = this.BuildCmsAuthValues(request); - var result = await this.communicationService.GetPreChargeDecisionCaseHistoryEventDetailsAsync(caseId, cmsAuthValues); + var result = await this.communicationService.GetPreChargeDecisionCaseHistoryEventDetailsAsync(caseId, cmsAuthValues, cancellationToken); if (result == null) { diff --git a/polaris-gateway/Functions/HouseKeeping/GetPreChargeDecisionByHistoryId.cs b/polaris-gateway/Functions/HouseKeeping/GetPreChargeDecisionByHistoryId.cs index 0e221ae7d..8017576b6 100644 --- a/polaris-gateway/Functions/HouseKeeping/GetPreChargeDecisionByHistoryId.cs +++ b/polaris-gateway/Functions/HouseKeeping/GetPreChargeDecisionByHistoryId.cs @@ -7,6 +7,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using System; using System.Diagnostics; using System.Net; +using System.Threading; using System.Threading.Tasks; using Common.Configuration; using Common.Constants; @@ -55,7 +56,7 @@ public class GetPreChargeDecisionByHistoryId( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.InternalServerError)] [Function(nameof(GetPreChargeDecisionByHistoryId))] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.PreChargeDecisionByHistoryId)] HttpRequest request, int caseId, int historyId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.PreChargeDecisionByHistoryId)] HttpRequest request, int caseId, int historyId, CancellationToken cancellationToken = default) { try { @@ -75,7 +76,7 @@ public async Task Run( // Build CMS auth values from cookie extracted from the request var cmsAuthValues = this.BuildCmsAuthValues(request); - var result = await this.communicationService.GetPreChargeDecisionByHistoryId(caseId,historyId, cmsAuthValues).ConfigureAwait(true); + var result = await this.communicationService.GetPreChargeDecisionByHistoryId(caseId,historyId, cmsAuthValues, cancellationToken).ConfigureAwait(true); if (result == null) { diff --git a/polaris-gateway/Functions/HouseKeeping/RenameMaterial.cs b/polaris-gateway/Functions/HouseKeeping/RenameMaterial.cs index fe9a987a5..69fa4ca58 100644 --- a/polaris-gateway/Functions/HouseKeeping/RenameMaterial.cs +++ b/polaris-gateway/Functions/HouseKeeping/RenameMaterial.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -59,7 +60,7 @@ public class RenameMaterial( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.UnprocessableEntity)] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.Unauthorized)] [Function("RenameMaterial")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = RestApi.RenameMaterial)] HttpRequest request, int caseId, int materialId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = RestApi.RenameMaterial)] HttpRequest request, int caseId, int materialId, CancellationToken cancellationToken = default) { try { diff --git a/polaris-gateway/Functions/HouseKeeping/SetMaterialReadStatus.cs b/polaris-gateway/Functions/HouseKeeping/SetMaterialReadStatus.cs index 922a76981..1837ab9c6 100644 --- a/polaris-gateway/Functions/HouseKeeping/SetMaterialReadStatus.cs +++ b/polaris-gateway/Functions/HouseKeeping/SetMaterialReadStatus.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -56,7 +57,7 @@ public class SetMaterialReadStatus( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.InternalServerError, Description = $"{LoggingConstants.HskUiLogPrefix} SetMaterialReadStatus function encountered an error")] [Function("SetMaterialReadStatus")] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = RestApi.ReadStatus)] HttpRequest request, int caseId, int materialId) + [HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = RestApi.ReadStatus)] HttpRequest request, int caseId, int materialId, CancellationToken cancellationToken = default) { try { @@ -90,7 +91,7 @@ public async Task Run( return new BadRequestObjectResult(nameof(SetMaterialReadStatusRequest.state)); } - SetMaterialReadStatusResponse? result = await this.communicationService.SetMaterialReadStatusAsync(materialId, setMaterialReadStatusRequest.state, cmsAuthValues).ConfigureAwait(true); + SetMaterialReadStatusResponse? result = await this.communicationService.SetMaterialReadStatusAsync(materialId, setMaterialReadStatusRequest.state, cmsAuthValues, cancellationToken:cancellationToken).ConfigureAwait(true); if (result?.CompleteCommunicationData?.Id == null) { diff --git a/polaris-gateway/Functions/HouseKeeping/UmaReclassify.cs b/polaris-gateway/Functions/HouseKeeping/UmaReclassify.cs index e1c8dc52d..fa9257bb0 100644 --- a/polaris-gateway/Functions/HouseKeeping/UmaReclassify.cs +++ b/polaris-gateway/Functions/HouseKeeping/UmaReclassify.cs @@ -9,6 +9,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using System.Diagnostics; using System.Linq; using System.Net; +using System.Threading; using System.Threading.Tasks; using Common.Configuration; using Common.Constants; @@ -60,7 +61,7 @@ public class UmaReclassify( [OpenApiRequestBody("application/json", typeof(IReadOnlyCollection), Description = "Body containing the Request info.")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.OK)] [Function("UmaReclassify")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.UmaReclassify)] HttpRequest req, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.UmaReclassify)] HttpRequest req, int caseId, CancellationToken cancellationToken = default) { try { @@ -146,10 +147,11 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, /// /// The ID of the case. /// The authorization values. + /// The cancellation token. /// A task that represents the asynchronous operation. The task result contains a collection of objects. - private async Task> GetCommunicationsAsync(int caseId, CmsAuthValues cmsAuthValues) + private async Task> GetCommunicationsAsync(int caseId, CmsAuthValues cmsAuthValues, CancellationToken cancellationToken = default) { this.logger.LogInformation($"{LoggingConstants.HskUiLogPrefix} Retrieving communications for caseId [{caseId}]..."); - return await this.communicationService.GetCommunicationsAsync(caseId, cmsAuthValues).ConfigureAwait(false); + return await this.communicationService.GetCommunicationsAsync(caseId, cmsAuthValues, cancellationToken).ConfigureAwait(false); } } diff --git a/polaris-gateway/Functions/HouseKeeping/UpdateExhibit.cs b/polaris-gateway/Functions/HouseKeeping/UpdateExhibit.cs index faf0527bd..24661648a 100644 --- a/polaris-gateway/Functions/HouseKeeping/UpdateExhibit.cs +++ b/polaris-gateway/Functions/HouseKeeping/UpdateExhibit.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -55,7 +56,7 @@ public class UpdateExhibit( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.UnprocessableEntity)] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.Unauthorized)] [Function("UpdateExhibit")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = RestApi.UpdateExhibit)] HttpRequest request, int caseId, int materialId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = RestApi.UpdateExhibit)] HttpRequest request, int caseId, int materialId, CancellationToken cancellationToken = default) { try { @@ -96,7 +97,8 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, UpdateExhibitResponse result = await this.communicationService.UpdateExhibitAsync( caseId, updateExhibitRequest, - cmsAuthValues).ConfigureAwait(true); + cmsAuthValues, + cancellationToken:cancellationToken).ConfigureAwait(true); if (result?.UpdateExhibitData?.Id == null) { diff --git a/polaris-gateway/Functions/HouseKeeping/UpdateStatement.cs b/polaris-gateway/Functions/HouseKeeping/UpdateStatement.cs index 67c963d81..5525d7eb0 100644 --- a/polaris-gateway/Functions/HouseKeeping/UpdateStatement.cs +++ b/polaris-gateway/Functions/HouseKeeping/UpdateStatement.cs @@ -8,6 +8,7 @@ namespace PolarisGateway.Functions.HouseKeeping; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; +using System.Threading; using System.Threading.Tasks; using Cps.Fct.Hk.Ui.Interfaces; using System.Diagnostics; @@ -54,7 +55,7 @@ public class UpdateStatement( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.UnprocessableEntity)] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.Unauthorized)] [Function("UpdateStatement")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = RestApi.UpdateStatement)] HttpRequest request, int caseId, int materialId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = RestApi.UpdateStatement)] HttpRequest request, int caseId, int materialId, CancellationToken cancellationToken = default) { try { @@ -78,7 +79,7 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, var updateStatementRequest = JsonConvert.DeserializeObject(requestBody); updateStatementRequest.MaterialId = materialId; - + if (updateStatementRequest is null) { return new BadRequestObjectResult("Statement request is null"); @@ -93,7 +94,8 @@ public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, UpdateStatementResponse result = await this.communicationService.UpdateStatementAsync( caseId, updateStatementRequest, - cmsAuthValues).ConfigureAwait(true); + cmsAuthValues, + cancellationToken: cancellationToken).ConfigureAwait(true); if (result?.UpdateStatementData?.Id == null) { diff --git a/polaris-gateway/Functions/LookupUrn.cs b/polaris-gateway/Functions/LookupUrn.cs index 18e8926ef..eb9cc27f8 100644 --- a/polaris-gateway/Functions/LookupUrn.cs +++ b/polaris-gateway/Functions/LookupUrn.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -40,14 +41,14 @@ public LookupUrn( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.LookupUrn)] HttpRequest req, int caseId) + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.LookupUrn)] HttpRequest req, int caseId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); var arg = _mdsArgFactory.CreateCaseIdArg(cmsAuthValues, correlationId, caseId); - var result = await _mdsClient.GetUrnFromCaseIdAsync(arg); + var result = await _mdsClient.GetUrnFromCaseIdAsync(arg, cancellationToken: cancellationToken); return new OkObjectResult(result); } diff --git a/polaris-gateway/Functions/PolarisPipelineCase.cs b/polaris-gateway/Functions/PolarisPipelineCase.cs index c4c89d65a..9dc936d25 100644 --- a/polaris-gateway/Functions/PolarisPipelineCase.cs +++ b/polaris-gateway/Functions/PolarisPipelineCase.cs @@ -11,6 +11,7 @@ using PolarisGateway.Extensions; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -43,7 +44,7 @@ public PolarisPipelineCase( [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(object), Summary = "Case found", Description = "Returns case details")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.Case)] HttpRequest req, string caseUrn, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.Case)] HttpRequest req, string caseUrn, int caseId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); diff --git a/polaris-gateway/Functions/PolarisPipelineCaseDelete.cs b/polaris-gateway/Functions/PolarisPipelineCaseDelete.cs index 5ff003b4b..7208ca1d9 100644 --- a/polaris-gateway/Functions/PolarisPipelineCaseDelete.cs +++ b/polaris-gateway/Functions/PolarisPipelineCaseDelete.cs @@ -4,6 +4,7 @@ using Common.Configuration; using PolarisGateway.Clients.Coordinator; using Microsoft.Azure.Functions.Worker; +using System.Threading; using System.Threading.Tasks; using PolarisGateway.Extensions; using Common.Telemetry; @@ -43,11 +44,12 @@ public PolarisPipelineCaseDelete( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = RestApi.Case)] HttpRequest req, string caseUrn, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = RestApi.Case)] HttpRequest req, string caseUrn, int caseId, CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); + cancellationToken.ThrowIfCancellationRequested(); return await (await _coordinatorClient.DeleteCaseAsync( caseUrn, diff --git a/polaris-gateway/Functions/PolarisPipelineCaseSearch.cs b/polaris-gateway/Functions/PolarisPipelineCaseSearch.cs index 131ee16b4..5be7ce27f 100644 --- a/polaris-gateway/Functions/PolarisPipelineCaseSearch.cs +++ b/polaris-gateway/Functions/PolarisPipelineCaseSearch.cs @@ -11,6 +11,7 @@ using PolarisGateway.Extensions; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -45,8 +46,10 @@ public PolarisPipelineCaseSearch( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseSearch)] HttpRequest req, string caseUrn, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseSearch)] HttpRequest req, string caseUrn, int caseId, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); + var correlationId = EstablishCorrelation(req); return await (await _coordinatorClient.SearchCase( diff --git a/polaris-gateway/Functions/PolarisPipelineCaseSearchIndexCount.cs b/polaris-gateway/Functions/PolarisPipelineCaseSearchIndexCount.cs index d5e50dc88..bf125db19 100644 --- a/polaris-gateway/Functions/PolarisPipelineCaseSearchIndexCount.cs +++ b/polaris-gateway/Functions/PolarisPipelineCaseSearchIndexCount.cs @@ -11,6 +11,7 @@ using PolarisGateway.Extensions; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -43,8 +44,9 @@ public PolarisPipelineCaseSearchIndexCount( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseSearchCount)] HttpRequest req, string caseUrn, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseSearchCount)] HttpRequest req, string caseUrn, int caseId, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); var correlationId = EstablishCorrelation(req); return await (await _coordinatorClient.GetCaseSearchIndexCount( diff --git a/polaris-gateway/Functions/PolarisPipelineGetCaseTracker.cs b/polaris-gateway/Functions/PolarisPipelineGetCaseTracker.cs index 297ede7bb..6e9ceda73 100644 --- a/polaris-gateway/Functions/PolarisPipelineGetCaseTracker.cs +++ b/polaris-gateway/Functions/PolarisPipelineGetCaseTracker.cs @@ -11,6 +11,7 @@ using PolarisGateway.Extensions; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -42,8 +43,9 @@ public PolarisPipelineGetCaseTracker( [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseTracker)] HttpRequest req, string caseUrn, int caseId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = RestApi.CaseTracker)] HttpRequest req, string caseUrn, int caseId, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); var correlationId = EstablishCorrelation(req); return await (await _coordinatorClient.GetTrackerGetCaseAsync( diff --git a/polaris-gateway/Functions/PolarisPipelineModifyDocument.cs b/polaris-gateway/Functions/PolarisPipelineModifyDocument.cs index 850ce3e6d..c60504261 100644 --- a/polaris-gateway/Functions/PolarisPipelineModifyDocument.cs +++ b/polaris-gateway/Functions/PolarisPipelineModifyDocument.cs @@ -1,6 +1,7 @@ using System; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using Common.Configuration; using Common.Dto.Request; @@ -48,13 +49,14 @@ public PolarisPipelineModifyDocument( [OpenApiParameter("caseId", In = ParameterLocation.Path, Type = typeof(int), Description = "The Id of the case to add a new action plan.", Required = true)] [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(object), Summary = "Case found", Description = "Returns case details")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.ModifyDocument)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.ModifyDocument)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId, CancellationToken cancellationToken = default) { var telemetryEvent = new DocumentModifiedEvent(caseId, materialId) { OperationName = nameof(PolarisPipelineModifyDocument), }; + cancellationToken.ThrowIfCancellationRequested(); var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); diff --git a/polaris-gateway/Functions/PolarisPipelineSaveDocumentRedactions.cs b/polaris-gateway/Functions/PolarisPipelineSaveDocumentRedactions.cs index 8fa65f305..14b853609 100644 --- a/polaris-gateway/Functions/PolarisPipelineSaveDocumentRedactions.cs +++ b/polaris-gateway/Functions/PolarisPipelineSaveDocumentRedactions.cs @@ -22,6 +22,7 @@ using System.Collections.Generic; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -60,13 +61,14 @@ public PolarisPipelineSaveDocumentRedactions( [OpenApiParameter("documentId", In = ParameterLocation.Path, Type = typeof(long), Description = "The document Id (version) of the material", Required = true)] [OpenApiResponseWithBody(HttpStatusCode.OK, "application/json", typeof(IEnumerable), Description = "OCR processing completed successfully")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = RestApi.RedactDocument)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = RestApi.RedactDocument)] HttpRequest req, string caseUrn, int caseId, string materialId, long documentId, CancellationToken cancellationToken = default) { var telemetryEvent = new RedactionRequestEvent(caseId, materialId) { OperationName = nameof(PolarisPipelineSaveDocumentRedactions), }; + cancellationToken.ThrowIfCancellationRequested(); var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); diff --git a/polaris-gateway/Functions/ReclassifyDocument.cs b/polaris-gateway/Functions/ReclassifyDocument.cs index d3e74b898..e331110c5 100644 --- a/polaris-gateway/Functions/ReclassifyDocument.cs +++ b/polaris-gateway/Functions/ReclassifyDocument.cs @@ -14,6 +14,7 @@ using PolarisGateway.TelemetryEvents; using PolarisGateway.Validators; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -50,8 +51,9 @@ public ReclassifyDocument( [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(object), Summary = "Document Note List", Description = "Returns list of document notes")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.ReclassifyDocument)] HttpRequest req, string caseUrn, int caseId, string materialId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = RestApi.ReclassifyDocument)] HttpRequest req, string caseUrn, int caseId, string materialId, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); var telemetryEvent = new DocumentReclassifiedEvent(caseId, materialId) { OperationName = nameof(ReclassifyDocument), diff --git a/polaris-gateway/Functions/RenameDocument.cs b/polaris-gateway/Functions/RenameDocument.cs index ae1449116..fcb8b846f 100644 --- a/polaris-gateway/Functions/RenameDocument.cs +++ b/polaris-gateway/Functions/RenameDocument.cs @@ -19,6 +19,7 @@ using System; using System.Linq; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -51,8 +52,9 @@ public RenameDocument(ILogger logger, [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(object), Summary = "Document rename", Description = "Returns list of document notes")] [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent, Summary = "Invalid request", Description = "Missing or invalid parameters")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = RestApi.RenameDocument)] HttpRequest req, string caseUrn, int caseId, string materialId) + public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "put", Route = RestApi.RenameDocument)] HttpRequest req, string caseUrn, int caseId, string materialId, CancellationToken cancellationToken = default) { + cancellationToken.ThrowIfCancellationRequested(); var telemetryEvent = new RenameDocumentRequestEvent(caseId, materialId) { OperationName = nameof(RenameDocument), diff --git a/polaris-gateway/Functions/ToggleIsUnusedDocument.cs b/polaris-gateway/Functions/ToggleIsUnusedDocument.cs index 741acfbda..0fd94c2a8 100644 --- a/polaris-gateway/Functions/ToggleIsUnusedDocument.cs +++ b/polaris-gateway/Functions/ToggleIsUnusedDocument.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; using System.Net; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Functions; @@ -42,7 +43,8 @@ public async Task Run( string caseUrn, int caseId, string materialId, - string isUnused) + string isUnused, + CancellationToken cancellationToken = default) { var correlationId = EstablishCorrelation(req); var cmsAuthValues = EstablishCmsAuthValues(req); @@ -57,6 +59,7 @@ public async Task Run( Urn = caseUrn, }; - return await _mdsClient.ToggleIsUnusedDocumentAsync(toggleIsUnusedDocumentDto) ? new OkResult() : new BadRequestResult(); + cancellationToken.ThrowIfCancellationRequested(); + return await _mdsClient.ToggleIsUnusedDocumentAsync(toggleIsUnusedDocumentDto, cancellationToken) ? new OkResult() : new BadRequestResult(); } } \ No newline at end of file diff --git a/polaris-gateway/Models/RedactionFileSizeOptions.cs b/polaris-gateway/Models/RedactionFileSizeOptions.cs new file mode 100644 index 000000000..132041863 --- /dev/null +++ b/polaris-gateway/Models/RedactionFileSizeOptions.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PolarisGateway.Models +{ + public class RedactionFileSizeOptions + { + public const string ConfigKey = "RedactionFileSize"; + + public double FileSizeLimitMb { get; set; } + } +} diff --git a/polaris-gateway/Services/Artefact/CacheService.cs b/polaris-gateway/Services/Artefact/CacheService.cs index 905869e85..fa2a0e3f2 100644 --- a/polaris-gateway/Services/Artefact/CacheService.cs +++ b/polaris-gateway/Services/Artefact/CacheService.cs @@ -29,10 +29,10 @@ public CacheService( return (result != null, result); } - public async Task UploadPdfAsync(int caseId, string materialId, long documentId, bool isOcrProcessed, Stream stream) + public async Task UploadPdfAsync(int caseId, string materialId, long documentId, bool isOcrProcessed, Stream stream, double fileSizeInMb) { var blobId = _blobTypeIdFactory.CreateBlobId(caseId, materialId, documentId, BlobType.Pdf); - await _polarisBlobStorageService.UploadBlobAsync(stream, blobId, isOcrProcessed); + await _polarisBlobStorageService.UploadBlobAsync(stream, blobId, isOcrProcessed, fileSizeInMb); } public async Task<(bool, T)> TryGetJsonObjectAsync(int caseId, string materialId, long documentId, BlobType blobType) @@ -47,5 +47,25 @@ public async Task UploadJsonObjectAsync(int caseId, string materialId, long d var blobId = _blobTypeIdFactory.CreateBlobId(caseId, materialId, documentId, blobType); await _polarisBlobStorageService.UploadObjectAsync(obj, blobId); } + + public async Task GetPdfSizeFromMetadataAsync(int caseId, string materialId, long documentId, bool isOcrProcessed) + { + var blobId = _blobTypeIdFactory.CreateBlobId( + caseId, + materialId, + documentId, + BlobType.Pdf); + + var metadata = await _polarisBlobStorageService.GetMetadataAsync(blobId); + + if (metadata != null && + metadata.TryGetValue("FileSizeInMb", out var value) && + double.TryParse(value, out var size)) + { + return size; + } + + return null; + } } } \ No newline at end of file diff --git a/polaris-gateway/Services/Artefact/Domain/ArtefactResult.cs b/polaris-gateway/Services/Artefact/Domain/ArtefactResult.cs index 2fc327a14..73c0b6ef1 100644 --- a/polaris-gateway/Services/Artefact/Domain/ArtefactResult.cs +++ b/polaris-gateway/Services/Artefact/Domain/ArtefactResult.cs @@ -21,4 +21,6 @@ public class ArtefactResult // Diagnostic property public bool? IsFromStorage { get; set; } -} \ No newline at end of file + + public bool? FileSizeExceedsLimit { get; set; } +} diff --git a/polaris-gateway/Services/Artefact/Domain/GetPdfRequest.cs b/polaris-gateway/Services/Artefact/Domain/GetPdfRequest.cs new file mode 100644 index 000000000..06a6b8f0d --- /dev/null +++ b/polaris-gateway/Services/Artefact/Domain/GetPdfRequest.cs @@ -0,0 +1,20 @@ +using System; + +namespace PolarisGateway.Services.Artefact.Domain; + +/// +/// Represents a request to retrieve a PDF artefact. +/// +/// The case URN identifier. +/// The case identifier. +/// The material identifier. +/// The document identifier (version) of the material. +/// Indicates whether OCR processing should be applied. +/// Indicates whether to force a refresh bypassing cache. +public record GetPdfRequest( + string Urn, + int CaseId, + string MaterialId, + long DocumentId, + bool IsOcrProcessed = false, + bool ForceRefresh = false); diff --git a/polaris-gateway/Services/Artefact/Factories/ArtefactServiceResponseFactory.cs b/polaris-gateway/Services/Artefact/Factories/ArtefactServiceResponseFactory.cs index 5b4604545..b428a7375 100644 --- a/polaris-gateway/Services/Artefact/Factories/ArtefactServiceResponseFactory.cs +++ b/polaris-gateway/Services/Artefact/Factories/ArtefactServiceResponseFactory.cs @@ -18,6 +18,17 @@ public ArtefactResult CreateOkfResult(T result, bool? isFromStorage) }; } + public ArtefactResult CreateOkResultWithLargeFileFlag(T result, bool? isFromStorage, bool? fileSizeExceedsLimit) + { + return new ArtefactResult + { + Status = ResultStatus.ArtefactAvailable, + Artefact = result, + IsFromStorage = isFromStorage, + FileSizeExceedsLimit = fileSizeExceedsLimit, + }; + } + public ArtefactResult CreateInterimResult(Guid continuationToken) { return new ArtefactResult diff --git a/polaris-gateway/Services/Artefact/Factories/IArtefactServiceResponseFactory.cs b/polaris-gateway/Services/Artefact/Factories/IArtefactServiceResponseFactory.cs index 4930f5914..49a047b32 100644 --- a/polaris-gateway/Services/Artefact/Factories/IArtefactServiceResponseFactory.cs +++ b/polaris-gateway/Services/Artefact/Factories/IArtefactServiceResponseFactory.cs @@ -8,6 +8,8 @@ public interface IArtefactServiceResponseFactory { ArtefactResult CreateOkfResult(T result, bool? isFromStorage); + ArtefactResult CreateOkResultWithLargeFileFlag(T result, bool? isFromStorage, bool? fileSizeExceedsLimit); + ArtefactResult CreateInterimResult(Guid continuationToken); ArtefactResult CreateFailedResult(PdfConversionStatus? pdfConversionStatus, int? httpStatusCode); diff --git a/polaris-gateway/Services/Artefact/ICacheService.cs b/polaris-gateway/Services/Artefact/ICacheService.cs index c055b83f1..664779873 100644 --- a/polaris-gateway/Services/Artefact/ICacheService.cs +++ b/polaris-gateway/Services/Artefact/ICacheService.cs @@ -8,10 +8,12 @@ public interface ICacheService { Task<(bool, Stream)> TryGetPdfAsync(int caseId, string materialId, long documentId, bool isOcrProcessed); - Task UploadPdfAsync(int caseId, string materialId, long documentId, bool isOcrProcessed, Stream stream); + Task UploadPdfAsync(int caseId, string materialId, long documentId, bool isOcrProcessed, Stream stream, double fileSizeInMb); Task<(bool, T)> TryGetJsonObjectAsync(int caseId, string materialId, long documentId, BlobType blobType); Task UploadJsonObjectAsync(int caseId, string materialId, long documentId, BlobType blobType, T obj); + + Task GetPdfSizeFromMetadataAsync(int caseId, string materialId, long documentId, bool isOcrProcessed); } } \ No newline at end of file diff --git a/polaris-gateway/Services/Artefact/IPdfArtefactService.cs b/polaris-gateway/Services/Artefact/IPdfArtefactService.cs index 0b54a7b05..2089a0711 100644 --- a/polaris-gateway/Services/Artefact/IPdfArtefactService.cs +++ b/polaris-gateway/Services/Artefact/IPdfArtefactService.cs @@ -1,11 +1,24 @@ using PolarisGateway.Services.Artefact.Domain; using System; using System.IO; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Services.Artefact; public interface IPdfArtefactService { - public Task> GetPdfAsync(string cmsAuthValues, Guid correlationId, string urn, int caseId, string materialId, long documentId, bool isOcrProcessed, bool forceRefresh = false); + /// + /// Retrieves a PDF artefact asynchronously. + /// + /// The request containing document identification and processing options. + /// The CMS authentication values. + /// The correlation identifier for tracking the request. + /// The cancellation token. + /// An artefact result containing the PDF stream. + public Task> GetPdfAsync( + GetPdfRequest request, + string cmsAuthValues, + Guid correlationId, + CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/polaris-gateway/Services/Artefact/OcrArtefactService.cs b/polaris-gateway/Services/Artefact/OcrArtefactService.cs index 59a71aba8..67b7fecd2 100644 --- a/polaris-gateway/Services/Artefact/OcrArtefactService.cs +++ b/polaris-gateway/Services/Artefact/OcrArtefactService.cs @@ -52,7 +52,15 @@ public async Task> GetOcrAsync(string cmsAuthValu } // If we are being called without an operationId and there is nothing in cache then we need to start a new OCR operation... - var pdfResult = await _pdfArtefactService.GetPdfAsync(cmsAuthValues, correlationId, urn, caseId, materialId, documentId, isOcrProcessed, forceRefresh); + var request = new GetPdfRequest( + Urn: urn, + CaseId: caseId, + MaterialId: materialId, + DocumentId: documentId, + IsOcrProcessed: isOcrProcessed, + ForceRefresh: forceRefresh); + + var pdfResult = await _pdfArtefactService.GetPdfAsync(request, cmsAuthValues, correlationId); if (pdfResult.Status != ResultStatus.ArtefactAvailable) { diff --git a/polaris-gateway/Services/Artefact/PdfArtefactService.cs b/polaris-gateway/Services/Artefact/PdfArtefactService.cs index a452f5284..f3f77969b 100644 --- a/polaris-gateway/Services/Artefact/PdfArtefactService.cs +++ b/polaris-gateway/Services/Artefact/PdfArtefactService.cs @@ -3,42 +3,53 @@ using Common.Extensions; using Common.Services.BlobStorage; using Common.Services.OcrService; +using Microsoft.AspNetCore.Http; using Microsoft.CodeAnalysis.Operations; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using PolarisGateway.Models; using PolarisGateway.Services.Artefact.Domain; using PolarisGateway.Services.Artefact.Factories; using System; using System.IO; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Services.Artefact; -public class PdfArtefactService : IPdfArtefactService +public class PdfArtefactService( + IOptions options, + ILogger logger, + ICacheService cacheService, + IArtefactServiceResponseFactory artefactServiceResponseFactory, + IPdfRetrievalService pdfRetrievalService, + IOcrService ocrService) + : IPdfArtefactService { - private readonly IArtefactServiceResponseFactory _artefactServiceResponseFactory; - private readonly ICacheService _cacheService; - private readonly IPdfRetrievalService _pdfRetrievalService; - private readonly IOcrService _ocrService; - - public PdfArtefactService( - ICacheService cacheService, - IArtefactServiceResponseFactory artefactServiceResponseFactory, - IPdfRetrievalService pdfRetrievalService, - IOcrService ocrService) + private readonly ILogger _logger = logger.ExceptionIfNull(); + private readonly RedactionFileSizeOptions _redactionFileSizeOptions = options.Value; + private readonly IArtefactServiceResponseFactory _artefactServiceResponseFactory = artefactServiceResponseFactory.ExceptionIfNull(); + private readonly ICacheService _cacheService = cacheService.ExceptionIfNull(); + private readonly IPdfRetrievalService _pdfRetrievalService = pdfRetrievalService.ExceptionIfNull(); + private readonly IOcrService _ocrService = ocrService.ExceptionIfNull(); + + public async Task> GetPdfAsync( + GetPdfRequest request, + string cmsAuthValues, + Guid correlationId, + CancellationToken cancellationToken = default) { - _cacheService = cacheService.ExceptionIfNull(); - _artefactServiceResponseFactory = artefactServiceResponseFactory.ExceptionIfNull(); - _pdfRetrievalService = pdfRetrievalService.ExceptionIfNull(); - _ocrService = ocrService.ExceptionIfNull(); - } + cancellationToken.ThrowIfCancellationRequested(); - public async Task> GetPdfAsync(string cmsAuthValues, Guid correlationId, string urn, int caseId, string materialId, long documentId, bool isOcrProcessed, bool forceRefresh = false) - { - if (!forceRefresh && await _cacheService.TryGetPdfAsync(caseId, materialId, documentId, isOcrProcessed) is (true, var stream)) + if (!request.ForceRefresh && await _cacheService.TryGetPdfAsync(request.CaseId, request.MaterialId, request.DocumentId, request.IsOcrProcessed) is (true, var stream)) { - return _artefactServiceResponseFactory.CreateOkfResult(stream, true); + var cachedFileSizeInMb = await _cacheService.GetPdfSizeFromMetadataAsync(request.CaseId, request.MaterialId, request.DocumentId, request.IsOcrProcessed); + + return ValidateFileSizeAndCreatePdfResult(stream, request.DocumentId, true, cachedFileSizeInMb ?? 0); } - var result = await _pdfRetrievalService.GetPdfStreamAsync(cmsAuthValues, correlationId, urn, caseId, materialId, documentId); + var result = await _pdfRetrievalService.GetPdfStreamAsync(cmsAuthValues, correlationId, request.Urn, request.CaseId, request.MaterialId, request.DocumentId); if (result.Status != PdfConversionStatus.DocumentConverted) { @@ -47,6 +58,8 @@ public async Task> GetPdfAsync(string cmsAuthValues, Guid // Read the PDF into a byte array byte[] pdfBytes; + double fileSizeInMb; + using (var buffer = new MemoryStream()) { await result.PdfStream.CopyToAsync(buffer); @@ -54,16 +67,35 @@ public async Task> GetPdfAsync(string cmsAuthValues, Guid } // Process OCR and upload to cache - await ProcessAndUploadOcrAsync(pdfBytes, caseId, materialId, documentId, correlationId); + await ProcessAndUploadOcrAsync(pdfBytes, request.CaseId, request.MaterialId, request.DocumentId, correlationId); // For PDF upload: use another fresh MemoryStream using (var uploadStream = new MemoryStream(pdfBytes)) { - await _cacheService.UploadPdfAsync(caseId, materialId, documentId, isOcrProcessed, uploadStream); + fileSizeInMb = uploadStream.Length / (1024.0 * 1024.0); + await _cacheService.UploadPdfAsync(request.CaseId, request.MaterialId, request.DocumentId, request.IsOcrProcessed, uploadStream, fileSizeInMb); + } + + var (_, pdfStream) = await _cacheService.TryGetPdfAsync(request.CaseId, request.MaterialId, request.DocumentId, request.IsOcrProcessed); + + return ValidateFileSizeAndCreatePdfResult(pdfStream, request.DocumentId, false, fileSizeInMb); + } + + private ArtefactResult ValidateFileSizeAndCreatePdfResult(Stream pdfStream, long documentId, bool fromCache, double fileSizeInMb) + { + if (fileSizeInMb > _redactionFileSizeOptions.FileSizeLimitMb) + { + _logger.LogInformation( + "Warning: document {DocumentId} has file size {FileSizeMb}MB which exceeds limit {FileSizeLimitMb}MB.", + documentId, + fileSizeInMb, + _redactionFileSizeOptions.FileSizeLimitMb + ); + + return _artefactServiceResponseFactory.CreateOkResultWithLargeFileFlag(pdfStream, fromCache, true); } - var (_, pdfStream) = await _cacheService.TryGetPdfAsync(caseId, materialId, documentId, isOcrProcessed); - return _artefactServiceResponseFactory.CreateOkfResult(pdfStream, false); + return _artefactServiceResponseFactory.CreateOkfResult(pdfStream, fromCache); } private async Task ProcessAndUploadOcrAsync(byte[] pdfBytes, int caseId, string materialId, long documentId, Guid correlationId) diff --git a/polaris-gateway/Services/MdsOrchestration/IMdsCaseOrchestrationService.cs b/polaris-gateway/Services/MdsOrchestration/IMdsCaseOrchestrationService.cs index aa82d1ea7..a4a717c59 100644 --- a/polaris-gateway/Services/MdsOrchestration/IMdsCaseOrchestrationService.cs +++ b/polaris-gateway/Services/MdsOrchestration/IMdsCaseOrchestrationService.cs @@ -1,12 +1,13 @@ using Common.Dto.Response.Case; using Ddei.Domain.CaseData.Args.Core; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Services.MdsOrchestration; public interface IMdsCaseOrchestrationService { - Task GetCase(MdsCaseIdentifiersArgDto arg); - Task> GetCases(MdsUrnArgDto arg); + Task GetCase(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default); + Task> GetCases(MdsUrnArgDto arg, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/polaris-gateway/Services/MdsOrchestration/MdsCaseDocumentsOrchestrationService.cs b/polaris-gateway/Services/MdsOrchestration/MdsCaseDocumentsOrchestrationService.cs index a5d47e1a1..baad44c1c 100644 --- a/polaris-gateway/Services/MdsOrchestration/MdsCaseDocumentsOrchestrationService.cs +++ b/polaris-gateway/Services/MdsOrchestration/MdsCaseDocumentsOrchestrationService.cs @@ -48,9 +48,11 @@ public async Task> GetCaseDocuments(MdsCaseIdentifiersA return Enumerable.Empty() .Concat(cmsDocuments.Select(MapDocument)) .Concat(pcdRequests.Select(MapPcdRequest)) - .Concat(defendantAndCharges.DefendantsAndCharges.Count() > 1 || defendantAndCharges.DefendantsAndCharges.Any(x => x.Charges.Count() > 1) - ? [MapDefendantAndCharges(defendantAndCharges)] - : [] + .Concat( + defendantAndCharges.DefendantsAndCharges.Any() || + defendantAndCharges.DefendantsAndCharges.Any(x => x.Charges.Any()) + ? [MapDefendantAndCharges(defendantAndCharges)] + : [] ); } diff --git a/polaris-gateway/Services/MdsOrchestration/MdsCaseOrchestrationService.cs b/polaris-gateway/Services/MdsOrchestration/MdsCaseOrchestrationService.cs index e4412494a..59533a3c8 100644 --- a/polaris-gateway/Services/MdsOrchestration/MdsCaseOrchestrationService.cs +++ b/polaris-gateway/Services/MdsOrchestration/MdsCaseOrchestrationService.cs @@ -7,6 +7,7 @@ using DdeiClient.Clients.Interfaces; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; namespace PolarisGateway.Services.MdsOrchestration; @@ -28,29 +29,29 @@ ICaseDetailsMapper caseDetailsMapper _caseDetailsMapper = caseDetailsMapper.ExceptionIfNull(); } - public async Task GetCase(MdsCaseIdentifiersArgDto arg) + public async Task GetCase(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default) { - var @case = await GetCaseDetails(arg); + var @case = await GetCaseDetails(arg, cancellationToken); return _caseDetailsMapper.MapCaseDetails(@case); } - public async Task> GetCases(MdsUrnArgDto arg) + public async Task> GetCases(MdsUrnArgDto arg, CancellationToken cancellationToken = default) { - var caseIdentifiers = await _mdsClient.ListCaseIdsAsync(arg); + var caseIdentifiers = await _mdsClient.ListCaseIdsAsync(arg, cancellationToken); var calls = caseIdentifiers.Select(async caseIdentifier => - await GetCaseDetails(_mdsArgFactory.CreateCaseArgFromUrnArg(arg, caseIdentifier.Id))); + await GetCaseDetails(_mdsArgFactory.CreateCaseArgFromUrnArg(arg, caseIdentifier.Id), cancellationToken)); var cases = await Task.WhenAll(calls); return cases.Select(@case => _caseDetailsMapper.MapCaseDetails(@case)); } - private async Task GetCaseDetails(MdsCaseIdentifiersArgDto arg) + private async Task GetCaseDetails(MdsCaseIdentifiersArgDto arg, CancellationToken cancellationToken = default) { - var getCaseSummaryTask = _mdsClient.GetCaseSummaryAsync(_mdsArgFactory.CreateCaseIdArg(arg.CmsAuthValues, arg.CorrelationId, arg.CaseId, arg.Urn)); - var getDefendantsAndChargesTask = _mdsClient.GetDefendantAndChargesAsync(arg); - var witnessesTask = _mdsClient.GetWitnessesAsync(arg); - var getPcdRequestTask = _mdsClient.GetPcdRequestsAsync(arg); + var getCaseSummaryTask = _mdsClient.GetCaseSummaryAsync(_mdsArgFactory.CreateCaseIdArg(arg.CmsAuthValues, arg.CorrelationId, arg.CaseId, arg.Urn), cancellationToken); + var getDefendantsAndChargesTask = _mdsClient.GetDefendantAndChargesAsync(arg, cancellationToken); + var witnessesTask = _mdsClient.GetWitnessesAsync(arg, cancellationToken); + var getPcdRequestTask = _mdsClient.GetPcdRequestsAsync(arg, cancellationToken); await Task.WhenAll(getCaseSummaryTask, getDefendantsAndChargesTask, witnessesTask, getPcdRequestTask); diff --git a/polaris-gateway/polaris-gateway.csproj b/polaris-gateway/polaris-gateway.csproj index bc0e7d35a..eec092d5f 100644 --- a/polaris-gateway/polaris-gateway.csproj +++ b/polaris-gateway/polaris-gateway.csproj @@ -18,12 +18,12 @@ - + - - - + + + PreserveNewest diff --git a/polaris-pipeline/Common.tests/Common.tests.csproj b/polaris-pipeline/Common.tests/Common.tests.csproj index fe0ba8f5a..b49475123 100644 --- a/polaris-pipeline/Common.tests/Common.tests.csproj +++ b/polaris-pipeline/Common.tests/Common.tests.csproj @@ -24,24 +24,24 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/polaris-pipeline/Common/Common.csproj b/polaris-pipeline/Common/Common.csproj index 6d834e186..779b5f98f 100644 --- a/polaris-pipeline/Common/Common.csproj +++ b/polaris-pipeline/Common/Common.csproj @@ -21,8 +21,8 @@ - - + + @@ -33,7 +33,7 @@ - + diff --git a/polaris-pipeline/Common/Constants/HttpHeaderKeys.cs b/polaris-pipeline/Common/Constants/HttpHeaderKeys.cs index bb97f7d8b..299b14053 100644 --- a/polaris-pipeline/Common/Constants/HttpHeaderKeys.cs +++ b/polaris-pipeline/Common/Constants/HttpHeaderKeys.cs @@ -6,5 +6,7 @@ public static class HttpHeaderKeys public const string CmsAuthValues = "Cms-Auth-Values"; public const string CmsToken = "Token"; public const string CmsCookie = "Cookies"; + public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers"; + public const string CpsFileTooLarge = "CPS-File-Too-Large"; } } diff --git a/polaris-pipeline/Common/Dto/Request/HouseKeeping/ReclassifyCaseMaterialServiceRequest.cs b/polaris-pipeline/Common/Dto/Request/HouseKeeping/ReclassifyCaseMaterialServiceRequest.cs new file mode 100644 index 000000000..44f1b1fd4 --- /dev/null +++ b/polaris-pipeline/Common/Dto/Request/HouseKeeping/ReclassifyCaseMaterialServiceRequest.cs @@ -0,0 +1,30 @@ +// +// Copyright (c) The Crown Prosecution Service. All rights reserved. +// + +namespace Common.Dto.Request.HouseKeeping; + +using System; + +/// +/// Represents a service-level request to reclassify a case material, containing all necessary parameters. +/// +/// The unique identifier of the case. +/// The unique identifier of the material to reclassify. +/// The type of classification to apply. +/// The document type id. +/// Flag to indicate if material is used or unused. +/// The material subject. +/// The statement request. +/// Exhibit request. +/// The correspondence identifier. +public record ReclassifyCaseMaterialServiceRequest( + int CaseId, + int MaterialId, + string Classification, + int DocumentTypeId, + bool Used, + string Subject, + ReclassifyStatementRequest? Statement = null, + ReclassifyExhibitRequest? Exhibit = null, + Guid CorrespondenceId = default); diff --git a/polaris-pipeline/Common/Services/BlobStorage/BlobStorageService.cs b/polaris-pipeline/Common/Services/BlobStorage/BlobStorageService.cs index 1754b12e0..f9f85c6bd 100644 --- a/polaris-pipeline/Common/Services/BlobStorage/BlobStorageService.cs +++ b/polaris-pipeline/Common/Services/BlobStorage/BlobStorageService.cs @@ -74,6 +74,15 @@ public async Task UploadBlobAsync(Stream stream, string blobName, IDictionary> GetMetadataAsync(string blobName) + { + var blobContainerClient = await GetBlobContainerClientOrThrow(); + var blobClient = blobContainerClient.GetBlobClient(blobName); + var properties = await blobClient.GetPropertiesAsync(); + + return properties.Value.Metadata; + } + public async Task UploadObjectAsync(T obj, string blobName) { var objectString = _jsonConvertWrapper.SerializeObject(obj); @@ -102,6 +111,8 @@ public async Task BlobExistsAsync(string blobName, IDictionary GetBlobContainerClientOrThrow() { var blobContainerClient = _blobServiceClient.GetBlobContainerClient(_blobServiceContainerName); diff --git a/polaris-pipeline/Common/Services/BlobStorage/IBlobStorageService.cs b/polaris-pipeline/Common/Services/BlobStorage/IBlobStorageService.cs index 8b66103ef..4b8122911 100644 --- a/polaris-pipeline/Common/Services/BlobStorage/IBlobStorageService.cs +++ b/polaris-pipeline/Common/Services/BlobStorage/IBlobStorageService.cs @@ -7,6 +7,7 @@ namespace Common.Services.BlobStorage public interface IBlobStorageService { Task UploadBlobAsync(Stream stream, string blobName, IDictionary metaData = null); + Task> GetMetadataAsync(string blobName); Task GetBlob(string blobName); Task TryGetBlobAsync(string blobName, IDictionary mustMatchMetadata = null); Task TryGetObjectAsync(string blobName); diff --git a/polaris-pipeline/Common/Services/BlobStorage/IPolarisBlobStorageService.cs b/polaris-pipeline/Common/Services/BlobStorage/IPolarisBlobStorageService.cs index 5e1f074f6..808aa488d 100644 --- a/polaris-pipeline/Common/Services/BlobStorage/IPolarisBlobStorageService.cs +++ b/polaris-pipeline/Common/Services/BlobStorage/IPolarisBlobStorageService.cs @@ -1,14 +1,17 @@ -using System.IO; +using System.Collections.Generic; +using System.IO; using System.Threading.Tasks; namespace Common.Services.BlobStorage { public interface IPolarisBlobStorageService { - Task UploadBlobAsync(Stream stream, BlobIdType blobId, bool? isOcred = null); + Task UploadBlobAsync(Stream stream, BlobIdType blobId, bool? isOcred = null, double? fileSizeInMb = null); Task UploadBlobAsync(Stream stream, BlobIdType blobId, int? pageIndex = null, int? maxDimensionPixel = null); + Task> GetMetadataAsync(BlobIdType blobId); + Task GetBlobAsync(BlobIdType blobId); Task TryGetBlobAsync(BlobIdType blobId, bool? mustBeOcred = null); @@ -20,5 +23,6 @@ public interface IPolarisBlobStorageService Task DeleteBlobsByPrefixAsync(int prefix); Task BlobExistsAsync(BlobIdType blobId, bool? mustBeOcred = null); + } } diff --git a/polaris-pipeline/Common/Services/BlobStorage/PolarisBlobStorageService.cs b/polaris-pipeline/Common/Services/BlobStorage/PolarisBlobStorageService.cs index 03e8d2bab..1228359c2 100644 --- a/polaris-pipeline/Common/Services/BlobStorage/PolarisBlobStorageService.cs +++ b/polaris-pipeline/Common/Services/BlobStorage/PolarisBlobStorageService.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; +using System.Text; using System.Threading.Tasks; namespace Common.Services.BlobStorage; @@ -30,8 +32,13 @@ public Task TryGetBlobAsync(BlobIdType blobId, bool? mustBeOcred = null) public Task UploadBlobAsync(Stream stream, BlobIdType blobId) => _blobStorageService.UploadBlobAsync(stream, GetBlobName(blobId)); - public Task UploadBlobAsync(Stream stream, BlobIdType blobId, bool? isOcred = null) - => _blobStorageService.UploadBlobAsync(stream, GetBlobName(blobId), CreateMetaData(isOcred)); + public Task UploadBlobAsync(Stream stream, BlobIdType blobId, bool? isOcred = null, double? fileSizeInMb = null) + => _blobStorageService.UploadBlobAsync(stream, GetBlobName(blobId), CreateMetaData(isOcred, fileSizeInMb)); + + public Task> GetMetadataAsync(BlobIdType blobId) + { + return _blobStorageService.GetMetadataAsync(GetBlobName(blobId)); + } public Task UploadBlobAsync(Stream stream, BlobIdType blobId, int? pageIndex = null, int? maxDimensionPixel = null) => _blobStorageService.UploadBlobAsync(stream, GetBlobName(blobId, pageIndex, maxDimensionPixel)); @@ -69,8 +76,20 @@ private static string GetSafePrefix(int caseId) return $"{caseId}/"; } - private static Dictionary CreateMetaData(bool? ocrFlag) - => ocrFlag == true - ? new Dictionary { { IsOcrProcessedInCms, true.ToString() } } - : null; + private static Dictionary CreateMetaData(bool? ocrFlag, double? fileSizeInMb = null) + { + var metadata = new Dictionary(); + + if (ocrFlag == true) + { + metadata[IsOcrProcessedInCms] = true.ToString(); + } + + if (fileSizeInMb.HasValue) + { + metadata["FileSizeInMb"] = fileSizeInMb.Value.ToString("F3"); // formatting + } + + return metadata; + } } \ No newline at end of file diff --git a/polaris-pipeline/DdeiClient.tests/MdsClient.tests.csproj b/polaris-pipeline/DdeiClient.tests/MdsClient.tests.csproj index 4d9c93afa..ae99cdc2c 100644 --- a/polaris-pipeline/DdeiClient.tests/MdsClient.tests.csproj +++ b/polaris-pipeline/DdeiClient.tests/MdsClient.tests.csproj @@ -26,20 +26,20 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/polaris-pipeline/coordinator.tests/coordinator.tests.csproj b/polaris-pipeline/coordinator.tests/coordinator.tests.csproj index d944f2a21..e3311b876 100644 --- a/polaris-pipeline/coordinator.tests/coordinator.tests.csproj +++ b/polaris-pipeline/coordinator.tests/coordinator.tests.csproj @@ -30,25 +30,25 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + \ No newline at end of file diff --git a/polaris-pipeline/coordinator/coordinator.csproj b/polaris-pipeline/coordinator/coordinator.csproj index 5a2aef124..317f84ed0 100644 --- a/polaris-pipeline/coordinator/coordinator.csproj +++ b/polaris-pipeline/coordinator/coordinator.csproj @@ -16,8 +16,8 @@ - - + + @@ -32,11 +32,11 @@ - - - + + + - + diff --git a/polaris-pipeline/pdf-generator.tests/pdf-generator.tests.csproj b/polaris-pipeline/pdf-generator.tests/pdf-generator.tests.csproj index 22efcf4cf..39cfb2d81 100644 --- a/polaris-pipeline/pdf-generator.tests/pdf-generator.tests.csproj +++ b/polaris-pipeline/pdf-generator.tests/pdf-generator.tests.csproj @@ -37,24 +37,24 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + diff --git a/polaris-pipeline/pdf-generator/pdf-generator.csproj b/polaris-pipeline/pdf-generator/pdf-generator.csproj index 717212ad7..ab1819963 100644 --- a/polaris-pipeline/pdf-generator/pdf-generator.csproj +++ b/polaris-pipeline/pdf-generator/pdf-generator.csproj @@ -12,12 +12,12 @@ - - + + - + diff --git a/polaris-pipeline/pdf-redactor.tests/pdf-redactor.tests.csproj b/polaris-pipeline/pdf-redactor.tests/pdf-redactor.tests.csproj index 26e5817d5..b6e7f391b 100644 --- a/polaris-pipeline/pdf-redactor.tests/pdf-redactor.tests.csproj +++ b/polaris-pipeline/pdf-redactor.tests/pdf-redactor.tests.csproj @@ -35,24 +35,24 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + diff --git a/polaris-pipeline/pdf-redactor/Services/DocumentRedaction/Aspose/RedactionImplementations/DirectImplementation.cs b/polaris-pipeline/pdf-redactor/Services/DocumentRedaction/Aspose/RedactionImplementations/DirectImplementation.cs index 355ade166..c48e22290 100644 --- a/polaris-pipeline/pdf-redactor/Services/DocumentRedaction/Aspose/RedactionImplementations/DirectImplementation.cs +++ b/polaris-pipeline/pdf-redactor/Services/DocumentRedaction/Aspose/RedactionImplementations/DirectImplementation.cs @@ -1,26 +1,39 @@ using Aspose.Pdf; using Aspose.Pdf.Annotations; -namespace pdf_redactor.Services.DocumentRedaction.Aspose.RedactionImplementations +namespace pdf_redactor.Services.DocumentRedaction.Aspose.RedactionImplementations; + +public class DirectImplementation : IRedactionImplementation { - public class DirectImplementation : IRedactionImplementation + public void AttachAnnotation(Page page, Rectangle rect) { - public void AttachAnnotation(Page page, Rectangle rect) + var redaction = new RedactionAnnotation(page, rect) { - var hardAnnotation = new RedactionAnnotation(page, rect) - { - FillColor = Color.Black, - }; - - page.Annotations.Add(hardAnnotation, true); - hardAnnotation.Redact(); - } + FillColor = Color.Black, + Color = Color.Black, + }; - public void FinaliseAnnotations(ref Document doc, Guid correlationId) { } + page.Annotations.Add(redaction, true); + } - public (ProviderType, string?) GetProviderType() + public void FinaliseAnnotations(ref Document doc, Guid correlationId) + { + foreach (Page page in doc.Pages) { - return (ProviderType.DirectRedaction, null); + var redactions = page.Annotations + .Where(a => a.AnnotationType == AnnotationType.Redaction) + .Cast() + .ToList(); + + foreach (var redaction in redactions) + { + redaction.Redact(); + } } } -} \ No newline at end of file + + public (ProviderType, string?) GetProviderType() + { + return (ProviderType.DirectRedaction, null); + } +} diff --git a/polaris-pipeline/pdf-redactor/Services/Extensions/ServiceCollectionExtension.cs b/polaris-pipeline/pdf-redactor/Services/Extensions/ServiceCollectionExtension.cs index 2519d4553..f457fe23b 100644 --- a/polaris-pipeline/pdf-redactor/Services/Extensions/ServiceCollectionExtension.cs +++ b/polaris-pipeline/pdf-redactor/Services/Extensions/ServiceCollectionExtension.cs @@ -17,7 +17,7 @@ public static void AddRedactionServices(this IServiceCollection services, IConfi // Aspose-specific services services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.Configure(configuration.GetSection(ImageConversionOptions.ConfigKey)); } diff --git a/polaris-pipeline/pdf-redactor/pdf-redactor.csproj b/polaris-pipeline/pdf-redactor/pdf-redactor.csproj index 8cc086a66..cad31a0ea 100644 --- a/polaris-pipeline/pdf-redactor/pdf-redactor.csproj +++ b/polaris-pipeline/pdf-redactor/pdf-redactor.csproj @@ -10,13 +10,13 @@ - + - + - + diff --git a/polaris-pipeline/pdf-thumbnail-generator/pdf-thumbnail-generator.csproj b/polaris-pipeline/pdf-thumbnail-generator/pdf-thumbnail-generator.csproj index 0215b30ba..d7bb0d057 100644 --- a/polaris-pipeline/pdf-thumbnail-generator/pdf-thumbnail-generator.csproj +++ b/polaris-pipeline/pdf-thumbnail-generator/pdf-thumbnail-generator.csproj @@ -16,12 +16,12 @@ - + - + diff --git a/polaris-pipeline/text-extractor.tests/text-extractor.tests.csproj b/polaris-pipeline/text-extractor.tests/text-extractor.tests.csproj index fbf86b6db..2f07cda3f 100644 --- a/polaris-pipeline/text-extractor.tests/text-extractor.tests.csproj +++ b/polaris-pipeline/text-extractor.tests/text-extractor.tests.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -23,25 +23,25 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + diff --git a/polaris-terraform/main-terraform/dev.tfvars b/polaris-terraform/main-terraform/dev.tfvars index c8d6c5ea9..b565ba5d2 100644 --- a/polaris-terraform/main-terraform/dev.tfvars +++ b/polaris-terraform/main-terraform/dev.tfvars @@ -180,3 +180,5 @@ cps_global_components = { sp_polaris_web_enabled = true # this SP has been temporarily disabled for Dev on the 29/07/2025. app_reg_owners = ["49a53165-9fa6-4f51-bebe-90ed1cc2e58f", "2cd0a11e-402b-4562-998c-2376d61bbb7f"] + +redaction_file_size_limit_mb = 15 diff --git a/polaris-terraform/main-terraform/function-gateway-staging.tf b/polaris-terraform/main-terraform/function-gateway-staging.tf index 7c1fa4449..b2c7fdfe7 100644 --- a/polaris-terraform/main-terraform/function-gateway-staging.tf +++ b/polaris-terraform/main-terraform/function-gateway-staging.tf @@ -43,6 +43,7 @@ resource "azurerm_linux_function_app_slot" "fa_polaris_staging1" { "PolarisPipelineCoordinatorBaseUrl" = "https://fa-${local.global_resource_name}-coordinator.azurewebsites.net/api/" "PolarisPipelineRedactPdfBaseUrl" = "https://fa-${local.global_resource_name}-pdf-generator.azurewebsites.net/api/" "PolarisPdfThumbnailGeneratorBaseUrl" = "https://fa-${local.global_resource_name}-pdf-thumb-gen.azurewebsites.net/api/" + "RedactionFileSize__FileSizeLimitMb" = var.redaction_file_size_limit_mb "TenantId" = data.azurerm_client_config.current.tenant_id "WEBSITE_ADD_SITENAME_BINDINGS_IN_APPHOST_CONFIG" = "1" "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" = azurerm_storage_account.sa_gateway.primary_connection_string diff --git a/polaris-terraform/main-terraform/function-gateway.tf b/polaris-terraform/main-terraform/function-gateway.tf index 648c89795..65dd32bb1 100644 --- a/polaris-terraform/main-terraform/function-gateway.tf +++ b/polaris-terraform/main-terraform/function-gateway.tf @@ -46,6 +46,7 @@ resource "azurerm_linux_function_app" "fa_polaris" { "PolarisPipelineCoordinatorBaseUrl" = "https://fa-${local.global_resource_name}-coordinator.azurewebsites.net/api/" "PolarisPipelineRedactPdfBaseUrl" = "https://fa-${local.global_resource_name}-pdf-generator.azurewebsites.net/api/" "PolarisPdfThumbnailGeneratorBaseUrl" = "https://fa-${local.global_resource_name}-pdf-thumb-gen.azurewebsites.net/api/" + "RedactionFileSize__FileSizeLimitMb" = var.redaction_file_size_limit_mb "TenantId" = data.azurerm_client_config.current.tenant_id "WEBSITE_ADD_SITENAME_BINDINGS_IN_APPHOST_CONFIG" = "1" "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" = azurerm_storage_account.sa_gateway.primary_connection_string diff --git a/polaris-terraform/main-terraform/prod.tfvars b/polaris-terraform/main-terraform/prod.tfvars index c6ea4db6e..dc72f6f20 100644 --- a/polaris-terraform/main-terraform/prod.tfvars +++ b/polaris-terraform/main-terraform/prod.tfvars @@ -73,7 +73,7 @@ cms_details = { cin5_upstream_cms_services_domain_name = "not-used-in-cin5.cps.gov.uk" } -auth_handover_whitelist = "/auth-refresh-inbound,https://cps.outsystemsenterprise.com/,https://housekeeping-fn.cps.gov.uk/,https://lcc-api.cps.gov.uk/,https://polaris.cps.gov.uk/auth-refresh-inbound?polaris-ui-url=/materials-ui" +auth_handover_whitelist = "/auth-refresh-inbound,https://cps.outsystemsenterprise.com/,https://housekeeping-fn.cps.gov.uk/,https://lcc-api.cps.gov.uk/,https://polaris.cps.gov.uk/auth-refresh-inbound?polaris-ui-url=/materials-ui,https://register-a-case-api.cps.gov.uk/" app_service_log_retention = 90 app_service_log_total_retention = 2555 @@ -179,3 +179,5 @@ cps_global_components = { sp_polaris_web_enabled = true app_reg_owners = ["49a53165-9fa6-4f51-bebe-90ed1cc2e58f", "2cd0a11e-402b-4562-998c-2376d61bbb7f"] + +redaction_file_size_limit_mb = 15 \ No newline at end of file diff --git a/polaris-terraform/main-terraform/qa.tfvars b/polaris-terraform/main-terraform/qa.tfvars index c1a655e8b..ef12a7654 100644 --- a/polaris-terraform/main-terraform/qa.tfvars +++ b/polaris-terraform/main-terraform/qa.tfvars @@ -180,3 +180,5 @@ cps_global_components = { sp_polaris_web_enabled = true app_reg_owners = ["49a53165-9fa6-4f51-bebe-90ed1cc2e58f", "2cd0a11e-402b-4562-998c-2376d61bbb7f"] + +redaction_file_size_limit_mb = 15 diff --git a/polaris-terraform/main-terraform/uat.tfvars b/polaris-terraform/main-terraform/uat.tfvars index 7155cf563..62322f63a 100644 --- a/polaris-terraform/main-terraform/uat.tfvars +++ b/polaris-terraform/main-terraform/uat.tfvars @@ -179,4 +179,6 @@ cps_global_components = { sp_polaris_web_enabled = true -app_reg_owners = ["49a53165-9fa6-4f51-bebe-90ed1cc2e58f", "2cd0a11e-402b-4562-998c-2376d61bbb7f"] \ No newline at end of file +app_reg_owners = ["49a53165-9fa6-4f51-bebe-90ed1cc2e58f", "2cd0a11e-402b-4562-998c-2376d61bbb7f"] + +redaction_file_size_limit_mb = 15 \ No newline at end of file diff --git a/polaris-terraform/main-terraform/variables.tf b/polaris-terraform/main-terraform/variables.tf index f739f446d..c2733c8cd 100644 --- a/polaris-terraform/main-terraform/variables.tf +++ b/polaris-terraform/main-terraform/variables.tf @@ -340,6 +340,10 @@ variable "hte_feature_flag" { type = bool } +variable "redaction_file_size_limit_mb" { + type = number +} + variable "image_conversion_redaction" { type = object({ resolution = number diff --git a/polaris-ui/src/app/features/cases/hooks/use-case-details-state/map-redaction-save-request.ts b/polaris-ui/src/app/features/cases/hooks/use-case-details-state/map-redaction-save-request.ts index 691f5e3bc..a03ca9f7f 100644 --- a/polaris-ui/src/app/features/cases/hooks/use-case-details-state/map-redaction-save-request.ts +++ b/polaris-ui/src/app/features/cases/hooks/use-case-details-state/map-redaction-save-request.ts @@ -115,7 +115,7 @@ export const mapSearchPIISaveRedactionObject = ( const piiData = Object.entries(piiCategoryGroupedHighlights).map( ([key, value]) => { return { - polarisCategory: value[0].redactionType.name, + polarisCategory: value[0].redactionType?.name, providerCategory: key, countSuggestions: value.length, countAccepted: value.filter( diff --git a/polaris-ui/src/app/features/cases/presentation/case-details/pdf-tabs/HeaderAttachmentMode.tsx b/polaris-ui/src/app/features/cases/presentation/case-details/pdf-tabs/HeaderAttachmentMode.tsx index 38952d750..802977e8f 100644 --- a/polaris-ui/src/app/features/cases/presentation/case-details/pdf-tabs/HeaderAttachmentMode.tsx +++ b/polaris-ui/src/app/features/cases/presentation/case-details/pdf-tabs/HeaderAttachmentMode.tsx @@ -36,7 +36,7 @@ export const HeaderAttachmentMode: React.FC = ({ handleAttachmentClick(item.documentId); }} > - {item.name} + {item?.name} {index < attachments.length - 1 && ( , )} diff --git a/polaris-ui/src/app/features/cases/presentation/case-details/portals/DeletePage.tsx b/polaris-ui/src/app/features/cases/presentation/case-details/portals/DeletePage.tsx index a9536cdac..a6299456a 100644 --- a/polaris-ui/src/app/features/cases/presentation/case-details/portals/DeletePage.tsx +++ b/polaris-ui/src/app/features/cases/presentation/case-details/portals/DeletePage.tsx @@ -78,7 +78,7 @@ export const DeletePage: React.FC = ({ trackEvent("Delete Page", { documentId: documentId, pageNumber: pageNumber, - reason: redactionType.name, + reason: redactionType?.name, }); setShowModal(false); }; diff --git a/polaris-ui/src/app/features/cases/presentation/case-details/reclassify/ReclassifyStage2.tsx b/polaris-ui/src/app/features/cases/presentation/case-details/reclassify/ReclassifyStage2.tsx index ee2273919..3c20e59df 100644 --- a/polaris-ui/src/app/features/cases/presentation/case-details/reclassify/ReclassifyStage2.tsx +++ b/polaris-ui/src/app/features/cases/presentation/case-details/reclassify/ReclassifyStage2.tsx @@ -257,10 +257,10 @@ export const ReclassifyStage2: React.FC = ({ const handleStatementDateChange = (event: any) => { let type: "day" | "month" | "year" = "day"; - if (event.target.name === "reclassify-statement-date-month") { + if (event.target?.name === "reclassify-statement-date-month") { type = "month"; } - if (event.target.name === "reclassify-statement-date-year") { + if (event.target?.name === "reclassify-statement-date-year") { type = "year"; } if (type) diff --git a/polaris-ui/src/app/features/cases/presentation/case-details/redactionLog/RedactionLogContent.tsx b/polaris-ui/src/app/features/cases/presentation/case-details/redactionLog/RedactionLogContent.tsx index c1b117dc1..c36127e1c 100644 --- a/polaris-ui/src/app/features/cases/presentation/case-details/redactionLog/RedactionLogContent.tsx +++ b/polaris-ui/src/app/features/cases/presentation/case-details/redactionLog/RedactionLogContent.tsx @@ -45,6 +45,7 @@ export type ErrorState = { underRedaction: boolean; overRedaction: boolean; notes: boolean; + isSelectedRedactionOfOtherTypeAndNotesBlank: boolean; }; type RedactionLogContentProps = { @@ -89,6 +90,9 @@ export const RedactionLogContent: React.FC = ({ redactionLogMappingsData, handleCloseRedactionLog, }) => { + const isSelectedRedactionOfOtherType = savedRedactionTypes.some( + (x) => x.name === "Other" + ); const errorSummaryRef = useRef(null); const trackEvent = useAppInsightsTrackEvent(); const [savingRedactionLog, setSavingRedactionLog] = useState(false); @@ -102,6 +106,7 @@ export const RedactionLogContent: React.FC = ({ underRedaction: false, overRedaction: false, notes: false, + isSelectedRedactionOfOtherTypeAndNotesBlank: false, }); const [defaultValues, setDefaultValues] = useState({ cpsArea: "", @@ -241,7 +246,7 @@ export const RedactionLogContent: React.FC = ({ ); }; - const supportingNotesGuidanceContent = () => { + const SupportingNotesGuidanceContent = () => { return (

@@ -254,10 +259,6 @@ export const RedactionLogContent: React.FC = ({

  • Avoid recording full names
  • Do not record sensitive personal data
  • -
  • - {`Supporting notes optional - ${NOTES_MAX_CHARACTERS} characters - maximum`} -
  • ); @@ -453,6 +454,12 @@ export const RedactionLogContent: React.FC = ({ href: "#redaction-log-notes", "data-testid": "redaction-log-notes-link", }; + case "isSelectedRedactionOfOtherTypeAndNotesBlank": + return { + children: `Please enter required Supporting notes`, + href: "#redaction-log-notes", + "data-testid": "redaction-log-notes-link", + }; } }; @@ -607,6 +614,9 @@ export const RedactionLogContent: React.FC = ({ documentType: !getValues("documentType"), chargeStatus: !getValues("chargeStatus"), notes: getValues("notes").length > NOTES_MAX_CHARACTERS, + isSelectedRedactionOfOtherTypeAndNotesBlank: + isSelectedRedactionOfOtherType && + getValues("notes").trim().length === 0, })); handleSubmit( (data) => { @@ -624,7 +634,6 @@ export const RedactionLogContent: React.FC = ({ if (errorSummaryRef.current) { (errorSummaryRef?.current as HTMLButtonElement).focus(); } - console.log("error", errors); } )(event); }} @@ -870,13 +879,18 @@ export const RedactionLogContent: React.FC = ({ ariaDescription="Guidance on adding optional supporting notes for redaction log" dataTestId="guidance-supporting-notes" > - {supportingNotesGuidanceContent()} + errorState.notes !== true }, + validate: { + required: () => errorState.notes !== true, + isSelectedRedactionOfOtherTypeAndNotBlank: (val) => + errorState.isSelectedRedactionOfOtherTypeAndNotesBlank !== + true, + }, }} render={({ field }) => { return ( diff --git a/polaris-ui/src/app/features/cases/presentation/case-details/redactionLog/UnderRedactionContent.tsx b/polaris-ui/src/app/features/cases/presentation/case-details/redactionLog/UnderRedactionContent.tsx index df3a66cd3..8ae6c9da9 100644 --- a/polaris-ui/src/app/features/cases/presentation/case-details/redactionLog/UnderRedactionContent.tsx +++ b/polaris-ui/src/app/features/cases/presentation/case-details/redactionLog/UnderRedactionContent.tsx @@ -14,11 +14,14 @@ export const UnderRedactionContent: React.FC = ({ const redactionSummary = useMemo(() => { const groupedRedactions = savedRedactionTypes.reduce( (acc, redactionType) => { - if (!acc[redactionType.name]) { - acc[redactionType.name] = 1; + const redactionTypeName = redactionType.name; + if (!redactionTypeName) return acc; + + if (!acc[redactionTypeName]) { + acc[redactionTypeName] = 1; return acc; } - acc[redactionType.name] = acc[redactionType.name] + 1; + acc[redactionTypeName] = acc[redactionTypeName] + 1; return acc; }, {} as Record