From b5edd55420d843c9f8b9c87864bc74c18667e7cf Mon Sep 17 00:00:00 2001 From: Zeeshaun Date: Mon, 27 Apr 2026 10:50:07 -0500 Subject: [PATCH] Updating --- LCU.API.Internal.FileOperations/.gitignore | 330 ++++++++++++++++++ .../FileDataEncryptionTests.cs | 132 +++++++ ...nal.FileOperations.IntegrationTests.csproj | 17 + .../LCU.Api.Internal.FileOperations.sln | 46 +++ .../Configurations/ApiConfiguration.cs | 13 + .../Consumers/FileOperationsConsumer.cs | 43 +++ .../DirectFileInformationController.cs | 60 ++++ .../DirectFileOperationsController.cs | 146 ++++++++ .../Controllers/DynamicDirectoryController.cs | 45 +++ .../Controllers/DynamicFileController.cs | 59 ++++ .../Controllers/ExcelController.cs | 62 ++++ .../Controllers/FileOperationsController.cs | 292 ++++++++++++++++ .../Controllers/FileValidationController.cs | 141 ++++++++ .../Controllers/IndexFileController.cs | 51 +++ .../Controllers/PdfController.cs | 185 ++++++++++ .../Controllers/StatusController.cs | 39 +++ .../Extensions/ModelStateMessages.cs | 16 + .../LCU.Api.Internal.FileOperations.csproj | 82 +++++ .../LCU.Api.Internal.FileOperations.xml | 42 +++ .../Models/IndexEvaluationRequest.cs | 128 +++++++ .../Program.cs | 227 ++++++++++++ .../Services/CryptoHelper.cs | 213 +++++++++++ .../Services/DirectFileOperationsService.cs | 46 +++ .../Services/DirectFileService.cs | 18 + .../Services/DynamicDirectoryService.cs | 26 ++ .../Services/ExcelServices/ExcelService.cs | 119 +++++++ .../Services/ExcelServices/IExcelService.cs | 9 + .../FileActionServices/AppendFileService.cs | 72 ++++ .../BulkOperationService.cs | 85 +++++ .../FileActionServices/CopyFileService.cs | 104 ++++++ .../CreateDirectoryService.cs | 66 ++++ .../DecryptFileDataService.cs | 68 ++++ .../DeleteDirectoryService.cs | 123 +++++++ .../DeleteEmptyDirectoryService.cs | 122 +++++++ .../FileActionServices/DeleteFileService.cs | 109 ++++++ .../EncryptFileDataService.cs | 68 ++++ .../ExtractIndexFileDataService.cs | 262 ++++++++++++++ .../FileActionServices/FileActionService.cs | 9 + .../FileActionServices/FileSizeService.cs | 85 +++++ .../FileActionServices/IActionService.cs | 9 + .../FileActionServices/IFileActionService.cs | 10 + .../FileActionServices/IFileSizeService.cs | 14 + .../FileActionServices/ModifyFileService.cs | 99 ++++++ .../FileActionServices/MoveFileService.cs | 105 ++++++ .../FileActionServices/OperationService.cs | 30 ++ .../RenameDirectoryService.cs | 67 ++++ .../FileActionServices/RenameFileService.cs | 85 +++++ .../FileActionServices/UnzipFileService.cs | 152 ++++++++ .../UnzipPasswordProtectedFileService.cs | 165 +++++++++ .../FileActionServices/ValidateFileService.cs | 108 ++++++ .../ValidateFileSizeInRangeService.cs | 111 ++++++ .../FileActionServices/WriteFileService.cs | 78 +++++ .../FileActionServices/ZipDirectoryService.cs | 110 ++++++ .../Services/FileOperationsService.cs | 87 +++++ .../Services/IDirectFileOperationsService.cs | 11 + .../Services/IDirectFileService.cs | 7 + .../Services/IDynamicDirectoryService.cs | 8 + .../Services/IFileOperationsService.cs | 10 + .../PdfServices/BatchPdfParserService.cs | 93 +++++ .../Services/PdfServices/HtmlToPdfService.cs | 193 ++++++++++ .../PdfServices/IBatchPdfParserService.cs | 8 + .../Services/PdfServices/IHtmlToPdfService.cs | 7 + .../Services/PdfServices/IPdfParserService.cs | 9 + .../Services/PdfServices/PdfParserService.cs | 47 +++ .../IQueueFileOperationsService.cs | 10 + .../QueueFileOperationsService.cs | 65 ++++ .../Services/RequestResponseHelper.cs | 16 + .../Validations/DirectoryValidation.cs | 108 ++++++ .../Validations/FileValidation.cs | 223 ++++++++++++ .../Validations/PdfValidations.cs | 38 ++ .../appsettings.json | 122 +++++++ .../libman.json | 5 + .../DirectFileOperationsControllerTest.cs | 243 +++++++++++++ .../FileOperationsControllerTest.cs | 302 ++++++++++++++++ .../Controllers/IndexFileControllerTest.cs | 81 +++++ .../Controllers/PdfControllerTest.cs | 330 ++++++++++++++++++ ...CU.Api.Internal.FileOperationsTests.csproj | 21 ++ .../DirectFileOperationsServiceTest.cs | 79 +++++ .../Services/DirectFileServiceTest.cs | 27 ++ .../Services/FileOperationsServiceTest.cs | 126 +++++++ .../Validations/FileValidationTest.cs | 128 +++++++ .../Shared/DynamicNameBuilderTest.cs | 144 ++++++++ .../Shared/RegexNameHandlerTest.cs | 90 +++++ .../additional_test_args.runsettings | 27 ++ .../IndexEvaluationRequestVariablesConsts.cs | 71 ++++ .../Enums/Delimiters.cs | 12 + .../Enums/FileSizeTypes.cs | 17 + .../Enums/OperatorTypes.cs | 16 + ...brary.Internal.FileOperationsModels.csproj | 119 +++++++ .../Models/AppendFileRequest.cs | 40 +++ .../Models/BatchParsePdfRequest.cs | 64 ++++ .../Models/BulkOperationRequest.cs | 10 + .../Models/CopyFileRequest.cs | 7 + .../Models/CreateDirectoryRequest.cs | 38 ++ .../Models/DecryptFileDataRequest.cs | 72 ++++ .../Models/DeleteDirectoryRequest.cs | 42 +++ .../Models/DeleteEmptyDirectoryRequest.cs | 37 ++ .../Models/DeleteFileRequest.cs | 39 +++ .../Models/DynamicDirectoryListRequest.cs | 14 + .../Models/DynamicFileListRequest.cs | 11 + .../Models/EncryptFileDataRequest.cs | 66 ++++ .../ExcelModels/Requests/ExcelDataRequest.cs | 29 ++ .../ExcelModels/Requests/SheetRequest.cs | 37 ++ .../ExcelModels/Responses/CellResponse.cs | 9 + .../Responses/ExcelDataResponse.cs | 11 + .../ExcelModels/Responses/SheetResponse.cs | 14 + .../Models/ExcelModels/Responses/WorkBook.cs | 12 + .../Models/ExtractIndexFileDataRequest.cs | 57 +++ .../Models/FileOperationType.cs | 25 ++ .../Models/FileRequest.cs | 66 ++++ .../HtmlToPdfModels/HtmlToPdfRequest.cs | 63 ++++ .../Models/HtmlToPdfModels/TagValue.cs | 10 + .../Models/HtmlToPdfModels/ValueCollection.cs | 12 + .../HtmlToPdfModels/ValueCollectionItem.cs | 8 + .../Models/ModifyFileRequest.cs | 78 +++++ .../Models/MoveFileRequest.cs | 6 + .../Models/NameSettings/DateFormatSetting.cs | 8 + .../Models/NameSettings/DynamicNameSetting.cs | 11 + .../Models/NameSettings/RegexSetting.cs | 8 + .../Models/ParsePdfRequest.cs | 46 +++ .../Models/QueueRequest.cs | 11 + .../Models/RenameDirectoryRequest.cs | 62 ++++ .../Models/RenameFileRequest.cs | 62 ++++ .../Models/RequestResponse.cs | 19 + .../Models/Requests/FileDestinationRequest.cs | 29 ++ .../Models/Requests/FileSizeRequest.cs | 9 + .../Models/Requests/FileSourceRequest.cs | 35 ++ .../Requests/IFileDestinationRequest.cs | 6 + .../Models/Requests/IFileSourceRequest.cs | 6 + .../Models/Requests/ValidateFileRequest.cs | 14 + .../ValidateFileSizeInRangeRequest.cs | 12 + .../Models/Responses/FileSizeResponse.cs | 26 ++ .../Models/UnzipFileRequest.cs | 96 +++++ .../UnzipPasswordProtectedFileRequest.cs | 76 ++++ .../Models/WriteFileRequest.cs | 38 ++ .../Models/ZipDirectoryRequest.cs | 53 +++ .../Shared/Clone.cs | 18 + .../Shared/DynamicNameBuilder.cs | 130 +++++++ .../Shared/RegexNameHandler.cs | 69 ++++ LCU.API.Internal.FileOperations/README.md | 20 ++ .../azure-pipelines.yml | 58 +++ 141 files changed, 9602 insertions(+) create mode 100644 LCU.API.Internal.FileOperations/.gitignore create mode 100644 LCU.API.Internal.FileOperations/LCU.API.Internal.FileOperations.IntegrationTests/FileDataEncryptionTests.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.API.Internal.FileOperations.IntegrationTests/LCU.API.Internal.FileOperations.IntegrationTests.csproj create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations.sln create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Configurations/ApiConfiguration.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Consumers/FileOperationsConsumer.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DirectFileInformationController.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DirectFileOperationsController.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DynamicDirectoryController.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DynamicFileController.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/ExcelController.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/FileOperationsController.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/FileValidationController.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/IndexFileController.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/PdfController.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/StatusController.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Extensions/ModelStateMessages.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/LCU.Api.Internal.FileOperations.csproj create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/LCU.Api.Internal.FileOperations.xml create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Models/IndexEvaluationRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Program.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/CryptoHelper.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DirectFileOperationsService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DirectFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DynamicDirectoryService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/ExcelServices/ExcelService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/ExcelServices/IExcelService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/AppendFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/BulkOperationService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/CopyFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/CreateDirectoryService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DecryptFileDataService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteDirectoryService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteEmptyDirectoryService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/EncryptFileDataService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ExtractIndexFileDataService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/FileActionService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/FileSizeService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IActionService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IFileActionService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IFileSizeService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ModifyFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/MoveFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/OperationService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/RenameDirectoryService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/RenameFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/UnzipFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/UnzipPasswordProtectedFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ValidateFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ValidateFileSizeInRangeService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/WriteFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ZipDirectoryService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileOperationsService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDirectFileOperationsService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDirectFileService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDynamicDirectoryService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IFileOperationsService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/BatchPdfParserService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/HtmlToPdfService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IBatchPdfParserService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IHtmlToPdfService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IPdfParserService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/PdfParserService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/QueueServices/IQueueFileOperationsService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/QueueServices/QueueFileOperationsService.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/RequestResponseHelper.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/DirectoryValidation.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/FileValidation.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/PdfValidations.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/appsettings.json create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/libman.json create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/DirectFileOperationsControllerTest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/FileOperationsControllerTest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/IndexFileControllerTest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/PdfControllerTest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/LCU.Api.Internal.FileOperationsTests.csproj create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/DirectFileOperationsServiceTest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/DirectFileServiceTest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/FileOperationsServiceTest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/Validations/FileValidationTest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Shared/DynamicNameBuilderTest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Shared/RegexNameHandlerTest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/additional_test_args.runsettings create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Constants/IndexEvaluationRequestVariablesConsts.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/Delimiters.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/FileSizeTypes.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/OperatorTypes.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/LCU.Library.Internal.FileOperationsModels.csproj create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/AppendFileRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/BatchParsePdfRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/BulkOperationRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/CopyFileRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/CreateDirectoryRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DecryptFileDataRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteDirectoryRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteEmptyDirectoryRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteFileRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DynamicDirectoryListRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DynamicFileListRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/EncryptFileDataRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Requests/ExcelDataRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Requests/SheetRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/CellResponse.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/ExcelDataResponse.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/SheetResponse.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/WorkBook.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExtractIndexFileDataRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/FileOperationType.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/FileRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/HtmlToPdfRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/TagValue.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/ValueCollection.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/ValueCollectionItem.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ModifyFileRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/MoveFileRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/DateFormatSetting.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/DynamicNameSetting.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/RegexSetting.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ParsePdfRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/QueueRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RenameDirectoryRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RenameFileRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RequestResponse.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileDestinationRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileSizeRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileSourceRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/IFileDestinationRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/IFileSourceRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/ValidateFileRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/ValidateFileSizeInRangeRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Responses/FileSizeResponse.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/UnzipFileRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/UnzipPasswordProtectedFileRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/WriteFileRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ZipDirectoryRequest.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/Clone.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/DynamicNameBuilder.cs create mode 100644 LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/RegexNameHandler.cs create mode 100644 LCU.API.Internal.FileOperations/README.md create mode 100644 LCU.API.Internal.FileOperations/azure-pipelines.yml diff --git a/LCU.API.Internal.FileOperations/.gitignore b/LCU.API.Internal.FileOperations/.gitignore new file mode 100644 index 0000000..3e759b7 --- /dev/null +++ b/LCU.API.Internal.FileOperations/.gitignore @@ -0,0 +1,330 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ diff --git a/LCU.API.Internal.FileOperations/LCU.API.Internal.FileOperations.IntegrationTests/FileDataEncryptionTests.cs b/LCU.API.Internal.FileOperations/LCU.API.Internal.FileOperations.IntegrationTests/FileDataEncryptionTests.cs new file mode 100644 index 0000000..e9c4dc6 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.API.Internal.FileOperations.IntegrationTests/FileDataEncryptionTests.cs @@ -0,0 +1,132 @@ +using System; +using System.IO; +using LCU.Api.Internal.FileOperations.Services.FileActionServices; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using Org.BouncyCastle.Bcpg; +using Org.BouncyCastle.Bcpg.OpenPgp; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Security; + +namespace LCU.API.Internal.FileOperations.IntegrationTests +{ + class FileDataEncryptionTests + { + private const string TestPassword = "password"; + private const string PrivateKeyFile = "TestPrivateKey"; + private const string PublicKeyFile = "TestPublicKey"; + private const string TestFile = "TestTextFile.txt"; + private const string TestFileContents = "This text will be saved to a temporary test file for encryption/comparison to resulting files from this integration test"; + private const string TestEncryptedFile = "TestTextFile.pgp"; + private const string TestDecryptedFile = "TestTextFile-Decrypted"; + private readonly string _basePath; + private readonly EncryptFileDataService _encryptFileDataService; + private readonly DecryptFileDataService _decryptFileDataService; + private readonly ServicesFactoryConfiguration _servicesFactoryConfiguration; + + FileDataEncryptionTests() + { + _encryptFileDataService = new EncryptFileDataService(_servicesFactoryConfiguration); + _decryptFileDataService = new DecryptFileDataService(_servicesFactoryConfiguration); + _basePath = Path.Combine(Directory.GetCurrentDirectory(), "TempFileFolder"); + Directory.CreateDirectory(_basePath); + GenerateTestKeys("Test@landmarkcu.com", TestPassword, Path.Combine(_basePath, PrivateKeyFile), Path.Combine(_basePath, PublicKeyFile)); + File.WriteAllText(Path.Combine(_basePath, TestFile), TestFileContents); + } + + static void Main() + { + var fileDataEncryptionTests = new FileDataEncryptionTests(); + + var encryptFileDataRequest = new EncryptFileDataRequest + { + SourcePath = fileDataEncryptionTests._basePath, + SourceFile = TestFile, + KeySourcePath = fileDataEncryptionTests._basePath, + KeySourceFile = PublicKeyFile, + DestinationPath = fileDataEncryptionTests._basePath + }; + + var decryptFileDataRequest = new DecryptFileDataRequest + { + SourcePath = fileDataEncryptionTests._basePath, + SourceFile = TestEncryptedFile, + KeySourcePath = fileDataEncryptionTests._basePath, + KeySourceFile = PrivateKeyFile, + DestinationPath = fileDataEncryptionTests._basePath, + DestinationFile = TestDecryptedFile, + PassPhrase = TestPassword + }; + + var encryptResult = fileDataEncryptionTests._encryptFileDataService.PerformRequest(encryptFileDataRequest); + var decryptResult = fileDataEncryptionTests._decryptFileDataService.PerformRequest(decryptFileDataRequest); + + Console.WriteLine($"Encrypted Request Is Valid: {encryptResult.IsSuccessful}"); + Console.WriteLine($"Decrypted Request Is Valid: {decryptResult.IsSuccessful}"); + + var encryptedFileContents = File.ReadAllText(Path.Combine(fileDataEncryptionTests._basePath, TestEncryptedFile)); + var decryptedFileContents = File.ReadAllText(Path.Combine(fileDataEncryptionTests._basePath, TestDecryptedFile)); + + Console.WriteLine($"Test File Contents: {TestFileContents}"); + Console.WriteLine($"Encrypted File Contents: {encryptedFileContents}"); + Console.WriteLine($"Decrypted File Contents: {decryptedFileContents}"); + + Console.WriteLine("Complete"); + } + + private static void GenerateTestKeys(string identity, string passPhrase, string secretKeyOutputPath, string publicKeyOutputPath) + { + var keyPairGenerator = new RsaKeyPairGenerator(); + keyPairGenerator.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024)); + var keyPair = keyPairGenerator.GenerateKeyPair(); + + using var secretOut = new FileInfo(secretKeyOutputPath).OpenWrite(); + using var publicOut = new FileInfo(publicKeyOutputPath).OpenWrite(); + ExportGeneratedKeyPair(secretOut, publicOut, keyPair.Public, keyPair.Private, identity, passPhrase.ToCharArray(), true); + } + + private static void ExportGeneratedKeyPair( + Stream secretOut, + Stream publicOut, + AsymmetricKeyParameter publicKey, + AsymmetricKeyParameter privateKey, + string identity, + char[] passPhrase, + bool armor) + { + if (armor) + { + secretOut = new ArmoredOutputStream(secretOut); + } + + var pgpSecretKey = new PgpSecretKey + ( + PgpSignature.DefaultCertification, + PublicKeyAlgorithmTag.RsaGeneral, + publicKey, + privateKey, + DateTime.Now, + identity, + SymmetricKeyAlgorithmTag.Cast5, + passPhrase, + null, + null, + new SecureRandom() + ); + + pgpSecretKey.Encode(secretOut); + secretOut.Close(); + + if (armor) + { + publicOut = new ArmoredOutputStream(publicOut); + } + + var pgpPublicKey = pgpSecretKey.PublicKey; + pgpPublicKey.Encode(publicOut); + publicOut.Close(); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.API.Internal.FileOperations.IntegrationTests/LCU.API.Internal.FileOperations.IntegrationTests.csproj b/LCU.API.Internal.FileOperations/LCU.API.Internal.FileOperations.IntegrationTests/LCU.API.Internal.FileOperations.IntegrationTests.csproj new file mode 100644 index 0000000..64b0382 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.API.Internal.FileOperations.IntegrationTests/LCU.API.Internal.FileOperations.IntegrationTests.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + + + + + + + + + + + + diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations.sln b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations.sln new file mode 100644 index 0000000..0bb6c09 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34723.18 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Api.Internal.FileOperations", "LCU.Api.Internal.FileOperations\LCU.Api.Internal.FileOperations.csproj", "{1C92A326-91A3-4904-B3EF-B766E10E7191}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Api.Internal.FileOperationsTests", "LCU.Api.Internal.FileOperationsTests\LCU.Api.Internal.FileOperationsTests.csproj", "{98503299-44F3-410A-B61F-36BC1706F132}" + ProjectSection(ProjectDependencies) = postProject + {1C92A326-91A3-4904-B3EF-B766E10E7191} = {1C92A326-91A3-4904-B3EF-B766E10E7191} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Library.Internal.FileOperationsModels", "LCU.Library.Internal.FileOperationsModels\LCU.Library.Internal.FileOperationsModels.csproj", "{0D3255ED-449B-4F11-861B-5C9479520ED5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.API.Internal.FileOperations.IntegrationTests", "LCU.API.Internal.FileOperations.IntegrationTests\LCU.API.Internal.FileOperations.IntegrationTests.csproj", "{6D9C8EE6-5A6A-47F8-8DB0-DB757B154176}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1C92A326-91A3-4904-B3EF-B766E10E7191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C92A326-91A3-4904-B3EF-B766E10E7191}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C92A326-91A3-4904-B3EF-B766E10E7191}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C92A326-91A3-4904-B3EF-B766E10E7191}.Release|Any CPU.Build.0 = Release|Any CPU + {98503299-44F3-410A-B61F-36BC1706F132}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {98503299-44F3-410A-B61F-36BC1706F132}.Debug|Any CPU.Build.0 = Debug|Any CPU + {98503299-44F3-410A-B61F-36BC1706F132}.Release|Any CPU.ActiveCfg = Release|Any CPU + {98503299-44F3-410A-B61F-36BC1706F132}.Release|Any CPU.Build.0 = Release|Any CPU + {0D3255ED-449B-4F11-861B-5C9479520ED5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D3255ED-449B-4F11-861B-5C9479520ED5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D3255ED-449B-4F11-861B-5C9479520ED5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D3255ED-449B-4F11-861B-5C9479520ED5}.Release|Any CPU.Build.0 = Release|Any CPU + {6D9C8EE6-5A6A-47F8-8DB0-DB757B154176}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D9C8EE6-5A6A-47F8-8DB0-DB757B154176}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D9C8EE6-5A6A-47F8-8DB0-DB757B154176}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D9C8EE6-5A6A-47F8-8DB0-DB757B154176}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {56A51B7E-A90A-4BFF-85D1-19ECB769AF6A} + EndGlobalSection +EndGlobal diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Configurations/ApiConfiguration.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Configurations/ApiConfiguration.cs new file mode 100644 index 0000000..6d5af09 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Configurations/ApiConfiguration.cs @@ -0,0 +1,13 @@ +namespace LCU.Api.Internal.FileOperations.Configurations; + +public class ApiConfiguration +{ + public string AppName { get; set; } + public string SeqUrl { get; set; } + public string SeqApiKey { get; set; } + public int AttemptsLimit { get; set; } + public int MaxFileCheckAttempts { get; set; } + public string ChromePath { get; set; } + public string FileOperationsChromeProfileFolderPath { get; set; } + public string[] CorsAllowedOrigins { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Consumers/FileOperationsConsumer.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Consumers/FileOperationsConsumer.cs new file mode 100644 index 0000000..ac545b4 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Consumers/FileOperationsConsumer.cs @@ -0,0 +1,43 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Serilog; +using MassTransit; +using LCU.Api.Internal.FileOperations.Services; +using LCU.Library.Internal.Exceptions; +using LCU.Api.Internal.MaidBotLib.Models; +using LCU.Library.Internal.FileOperationsModels.Models; + +namespace LCU.Api.Internal.FileOperations.Consumers; + +public class FileOperationsConsumer : IConsumer +{ + private readonly IServiceScopeFactory _scopeFactory; + + public FileOperationsConsumer(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + } + + public async Task Consume(ConsumeContext context) + { + try + { + using var scope = _scopeFactory.CreateScope(); + await Task.Run(async () => await scope.ServiceProvider.GetRequiredService().HandleFileOperationsRequest(context.Message)); + } + catch (ServiceException) + { + //Masstransit handles the exception. https://masstransit-project.com/usage/exceptions.html#retry + throw; + } + catch (Exception exception) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), + Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Fatal(exception, $"Unexpected Failure in FileOperations Api"); + //Masstransit handles the exception. https://masstransit-project.com/usage/exceptions.html#retry + throw; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DirectFileInformationController.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DirectFileInformationController.cs new file mode 100644 index 0000000..3a57f7b --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DirectFileInformationController.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.Api.Internal.FileOperations.Extensions; +using LCU.Api.Internal.FileOperations.Services.FileActionServices; +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Models.Requests; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Serilog; + +namespace LCU.Api.Internal.FileOperations.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class DirectFileInformationController : ControllerBase +{ + private readonly IFileSizeService _fileSizeService; + + public DirectFileInformationController(IFileSizeService fileSizeService) + { + _fileSizeService = fileSizeService; + } + + [HttpPost("GetFileSize")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public IActionResult GetFileSize([FromBody] FileSizeRequest request) + { + try + { + + if (!ModelState.IsValid) + { + return BadRequest(ModelState.GetErrorMessages()); + } + + var validateResponse = _fileSizeService.PreQueueValidation(request); + if (!validateResponse.IsSuccessful) + { + return BadRequest($"Request is invalid. Response: {string.Join("\n ", validateResponse.MessageStack.ToArray())}"); + } + + var response = _fileSizeService.GetFileSize(request); + return Ok(response); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DirectFileOperationsController.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DirectFileOperationsController.cs new file mode 100644 index 0000000..e56580f --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DirectFileOperationsController.cs @@ -0,0 +1,146 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Serilog; +using LCU.Api.Internal.FileOperations.Services; +using LCU.Api.Internal.FileOperations.Services.FileActionServices; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.ApiResponse.Models; +namespace LCU.Api.Internal.FileOperations.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class DirectFileOperationsController : ControllerBase +{ + private readonly IDirectFileOperationsService _directFileOperationsService; + private readonly IActionService _moveFileService; + private readonly IActionService _writeFileService; + private readonly IActionService _createDirectoryRequestService; + private readonly IActionService _unzipFileRequestService; + private readonly IActionService _zipDirectoryRequestService; + private readonly IActionService _deleteDirectoryRequestService; + private readonly IActionService _deleteFileRequestService; + private readonly IActionService _appendFileService; + + public DirectFileOperationsController(IDirectFileOperationsService directFileOperationsService, + IActionService moveFileService, + IActionService writeFileRequest, + IActionService createDirectoryRequestService, + IActionService unzipFileRequestService, + IActionService zipDirectoryRequestService, + IActionService deleteDirectoryRequestService, + IActionService deleteFileRequestService, + IActionService appendFileService) + { + _directFileOperationsService = directFileOperationsService; + _moveFileService = moveFileService; + _writeFileService = writeFileRequest; + _createDirectoryRequestService = createDirectoryRequestService; + _unzipFileRequestService = unzipFileRequestService; + _zipDirectoryRequestService = zipDirectoryRequestService; + _deleteDirectoryRequestService = deleteDirectoryRequestService; + _deleteFileRequestService = deleteFileRequestService; + _appendFileService = appendFileService; + } + + [HttpPost("MoveFile")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ReturnResponse), 400)] + [ProducesResponseType(500)] + public async Task MoveFile([FromBody] MoveFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.Move, _moveFileService); + } + + [HttpPost("WriteFile")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ReturnResponse), 400)] + [ProducesResponseType(500)] + public async Task WriteFile([FromBody] WriteFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.WriteFile, _writeFileService); + } + + [HttpPost("CreateDirectory")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ReturnResponse), 400)] + [ProducesResponseType(500)] + public async Task CreateDirectory([FromBody] CreateDirectoryRequest request) + { + return await HandleRequestResponse(request, FileOperationType.CreateDirectory, _createDirectoryRequestService); + } + + [HttpPost("UnzipFile")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ReturnResponse), 400)] + [ProducesResponseType(500)] + public async Task UnzipFile([FromBody] UnzipFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.UnzipFile, _unzipFileRequestService); + } + + [HttpPost("ZipDirectory")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ReturnResponse), 400)] + [ProducesResponseType(500)] + public async Task ZipDirectory([FromBody] ZipDirectoryRequest request) + { + return await HandleRequestResponse(request, FileOperationType.ZipDirectory, _zipDirectoryRequestService); + } + + [HttpPost("DeleteDirectory")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ReturnResponse), 400)] + [ProducesResponseType(500)] + public async Task DeleteDirectory([FromBody] DeleteDirectoryRequest request) + { + return await HandleRequestResponse(request, FileOperationType.DeleteDirectory, _deleteDirectoryRequestService); + } + + + [HttpPost("DeleteFile")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ReturnResponse), 400)] + [ProducesResponseType(500)] + public async Task DeleteFile([FromBody] DeleteFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.Delete, _deleteFileRequestService); + } + + [HttpPost("AppendFile")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ReturnResponse), 400)] + [ProducesResponseType(500)] + public async Task AppendFile([FromBody] AppendFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.AppendFile, _appendFileService); + } + + public async Task HandleRequestResponse(TRequest originalRequest, + FileOperationType operationType, + IActionService service) where TRequest : ReturnResponse + { + try + { + var validateResponse = service.PreQueueValidation(originalRequest); + if (!validateResponse.IsSuccessful) + { + return BadRequest($"Request is invalid. Response: {String.Join("\n ", validateResponse.MessageStack.ToArray())}"); + } + + var result = await _directFileOperationsService.HandleDirectFileOperationsRequest(originalRequest, operationType); + return Ok(result); + } + catch (ServiceException serviceException) + { + return StatusCode(StatusCodes.Status500InternalServerError, serviceException.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DynamicDirectoryController.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DynamicDirectoryController.cs new file mode 100644 index 0000000..4e83891 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DynamicDirectoryController.cs @@ -0,0 +1,45 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Serilog; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Api.Internal.FileOperations.Services; +namespace LCU.Api.Internal.FileOperations.Controllers; + +[ApiController] +[Route("[controller]")] +public class DynamicDirectoryController : ControllerBase +{ + private readonly IDynamicDirectoryService _dynamicDirectoryService; + + public DynamicDirectoryController(IDynamicDirectoryService dynamicDirectoryService) + { + _dynamicDirectoryService = dynamicDirectoryService; + } + + [HttpPost("BuildDirectoryList")] + [ProducesResponseType(201)] + [ProducesResponseType(404)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public IActionResult BuildDirectoryList([FromBody] DynamicDirectoryListRequest request) + { + try + { + var result = _dynamicDirectoryService.BuildDirectoryList(request); + if (result == null || !result.Any()) + { + return NotFound(); + } + + return Ok(result); + } + catch (Exception ex) + { + Log.Error(ex, "Failure Build Dynamic Directory List"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DynamicFileController.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DynamicFileController.cs new file mode 100644 index 0000000..b336fec --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/DynamicFileController.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Serilog; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; + +namespace LCU.Api.Internal.FileOperations.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DynamicFileController : ControllerBase + { + /// Gets a list of files based on regex substitution. + /// + /// Example: + /// POST /api/BuildDynamicFileList + /// { + /// "path": "\\\\172.16.13.17\\Reports\\", + /// "filename": "Action_IndirectVehicleLoanSales_date_time.csv", + /// "dynamicFileSettings": { + /// "regexSettings": [ + /// { + /// "tag": "date", + /// "regex": "\\d+_\\d+_\\d+" + /// }, + /// { + /// "tag": "time", + /// "regex": "\\d+" + /// }], + /// "daysFromToday": 0 + /// } + /// } + /// + /// + /// + // POST api/BuildDynamicFileList + [HttpPost("BuildDynamicFileList")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public IActionResult BuildDynamicFileList([FromBody] DynamicFileListRequest dynamicFileListRequest) + { + try + { + var fileNames = RegexNameHandler.BuildFileList(dynamicFileListRequest.Path, + dynamicFileListRequest.Filename, + dynamicFileListRequest.DynamicFileSettings); + return Ok(fileNames); + } + catch (Exception ex) + { + Log.Error(ex, "Failure Build Dynamic File List"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/ExcelController.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/ExcelController.cs new file mode 100644 index 0000000..350ad34 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/ExcelController.cs @@ -0,0 +1,62 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Serilog; +using LCU.Library.Internal.Exceptions; +using LCU.Api.Internal.FileOperations.Services.ExcelServices; +using LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Requests; +using LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Responses; +using LCU.Api.Internal.FileOperations.Extensions; +namespace LCU.Api.Internal.FileOperations.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class ExcelController : ControllerBase +{ + private readonly IExcelService _excelService; + + public ExcelController(IExcelService excelService) + { + _excelService = excelService; + } + + /// + /// This endpoint is a direct file operations call to extract excel workbook data. The ExcelDataRequest contains + /// the file content in a base64 encoded string for an excel sheet and it will extract all data into a json object. + /// The request also has optional SheetRequests in which a SheetName and specific Rows and Columns can be selected. + /// + /// Notes: + /// - FileContent is required. + /// - SheetRequests are optional. + /// - If SheetRequests are added the SheetName is required. + /// - The SheetRequest's Rows and Columns are optional. + /// + /// + /// ExcelDataResponse + [HttpPost("ExtractExcelWorkBookData")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ExcelDataResponse), 400)] + [ProducesResponseType(500)] + public IActionResult ExtractExcelWorkBookData([FromBody] ExcelDataRequest request) + { + try + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState.GetErrorMessages()); + } + + var requestResponse = _excelService.ExtractExcelWorkBookData(request); + return Ok(requestResponse); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/FileOperationsController.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/FileOperationsController.cs new file mode 100644 index 0000000..22fc300 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/FileOperationsController.cs @@ -0,0 +1,292 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Serilog; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Api.Internal.FileOperations.Services.QueueServices; +using LCU.Api.Internal.FileOperations.Extensions; +using LCU.Api.Internal.FileOperations.Services; +using LCU.Api.Internal.FileOperations.Services.FileActionServices; +namespace LCU.Api.Internal.FileOperations.Controllers; + +[Route("api/[controller]")] +[ApiController] +public class FileOperationsController : ControllerBase +{ + private readonly IQueueFileOperationsService _queueFileOperationsService; + private readonly IActionService _moveFileService; + private readonly IActionService _copyFileService; + private readonly IActionService _deleteFileService; + private readonly IActionService _modifyFileService; + private readonly IActionService _renameFileService; + private readonly IActionService _zipDirectoryService; + private readonly IActionService _unzipFileService; + private readonly IActionService _unzipPasswordProtectedFileService; + private readonly IActionService _createDirectoryService; + private readonly IActionService _renameDirectoryService; + private readonly IActionService> _bulkMoveFileService; + private readonly IActionService _encryptFileDataService; + private readonly IActionService _decryptFileDataService; + private readonly IActionService _writeFileDataService; + private readonly IActionService _deleteDirectoryService; + private readonly IActionService _deleteEmptyDirectoryService; + private readonly IDirectFileService _directFileService; + + public FileOperationsController(IQueueFileOperationsService fileOperationsService, + IActionService moveFileService, + IActionService copyFileService, + IActionService modifyFileService, + IActionService deleteFileService, + IActionService renameFileService, + IActionService zipDirectoryService, + IActionService unzipFileService, + IActionService unzipPasswordProtectedFileService, + IActionService createDirectoryService, + IActionService renameDirectoryService, + IActionService> bulkMoveFileService, + IActionService encryptFileDataService, + IActionService decryptFileDataService, + IActionService writeFileDataService, + IActionService deleteDirectoryService, + IActionService deleteEmptyDirectoryService, + IDirectFileService directFileService) + { + _queueFileOperationsService = fileOperationsService; + _moveFileService = moveFileService; + _copyFileService = copyFileService; + _modifyFileService = modifyFileService; + _deleteFileService = deleteFileService; + _renameFileService = renameFileService; + _zipDirectoryService = zipDirectoryService; + _unzipFileService = unzipFileService; + _unzipPasswordProtectedFileService = unzipPasswordProtectedFileService; + _createDirectoryService = createDirectoryService; + _renameDirectoryService = renameDirectoryService; + _bulkMoveFileService = bulkMoveFileService; + _encryptFileDataService = encryptFileDataService; + _decryptFileDataService = decryptFileDataService; + _directFileService = directFileService; + _writeFileDataService = writeFileDataService; + _deleteDirectoryService = deleteDirectoryService; + _deleteEmptyDirectoryService = deleteEmptyDirectoryService; + } + + // Get api/{file} + [HttpGet("{filename}")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + public IActionResult GetFile([FromRoute] string filename) + { + if (string.IsNullOrWhiteSpace(filename)) + { + return BadRequest("filename is required"); + } + + try + { + var result = _directFileService.GetFileBytes(filename); + return new FileContentResult(result, "application/octet-stream"); + } + catch (ArgumentException ae) + { + Log.Error(ae, $"Request for bad file path {filename}"); + return BadRequest("File path not found"); + } + catch (Exception ex) + { + Log.Error(ex, $"Unexpected error retrieving file path {filename}"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + // POST api/MoveFile + [HttpPost("MoveFile")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task MoveFile([FromBody] MoveFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.Move, _moveFileService); + } + + // POST api/CopyFile + [HttpPost("CopyFile")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task CopyFile([FromBody] CopyFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.Copy, _copyFileService); + } + + // POST api/ModifyFile + [HttpPost("ModifyFile")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task ModifyFile([FromBody] ModifyFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.Modify, _modifyFileService); + } + + // POST api/DeleteFile + [HttpPost("DeleteFile")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task DeleteFile([FromBody] DeleteFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.Delete, _deleteFileService); + } + + // POST api/RenameFile + [HttpPost("RenameFile")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task RenameFile([FromBody] RenameFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.Rename, _renameFileService); + } + + // POST api/ZipDirectory + [HttpPost("ZipDirectory")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task ZipDirectory([FromBody] ZipDirectoryRequest request) + { + return await HandleRequestResponse(request, FileOperationType.ZipDirectory, _zipDirectoryService); + } + + // POST api/UnzipFile + [HttpPost("UnzipFile")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task UnzipFile([FromBody] UnzipFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.UnzipFile, _unzipFileService); + } + + // POST api/UnzipPasswordProtectedFile + [HttpPost("UnzipPasswordProtectedFile")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task UnzipPasswordProtectedFile([FromBody] UnzipPasswordProtectedFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.UnzipPasswordProtectedFile, _unzipPasswordProtectedFileService); + } + + // POST api/CreateDirectory + [HttpPost("CreateDirectory")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task CreateDirectory([FromBody] CreateDirectoryRequest request) + { + return await HandleRequestResponse(request, FileOperationType.CreateDirectory, _createDirectoryService); + } + + // POST api/RenameDirectory + [HttpPost("RenameDirectory")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task RenameDirectory([FromBody] RenameDirectoryRequest request) + { + return await HandleRequestResponse(request, FileOperationType.RenameDirectory, _renameDirectoryService); + } + + // POST api/BulkMoveFile + [HttpPost("BulkMoveFile")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task BulkMoveFile([FromBody] BulkOperationRequest request) + { + return await HandleRequestResponse(request, FileOperationType.BulkMoveFile, _bulkMoveFileService); + } + + // POST api/EncryptFileData + [HttpPost("EncryptFileData")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task EncryptFileData([FromBody] EncryptFileDataRequest request) + { + return await HandleRequestResponse(request, FileOperationType.EncryptFileData, _encryptFileDataService); + } + + // POST api/DecryptFileData + [HttpPost("DecryptFileData")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task DecryptFileData([FromBody] DecryptFileDataRequest request) + { + return await HandleRequestResponse(request, FileOperationType.DecryptFileData, _decryptFileDataService); + } + + // POST api/WriteFileData + [HttpPost("WriteFileData")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task WriteFileData([FromBody] WriteFileRequest request) + { + return await HandleRequestResponse(request, FileOperationType.WriteFile, _writeFileDataService); + } + + [HttpPost("DeleteDirectory")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task DeleteDirectory([FromBody] DeleteDirectoryRequest request) + { + return await HandleRequestResponse(request, FileOperationType.DeleteDirectory, _deleteDirectoryService); + } + + [HttpPost("DeleteEmptyDirectory")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task DeleteEmptyDirectory([FromBody] DeleteEmptyDirectoryRequest request) + { + return await HandleRequestResponse(request, FileOperationType.DeleteEmptyDirectory, _deleteEmptyDirectoryService); + } + + public async Task HandleRequestResponse(TRequest originalRequest, FileOperationType operationType, IActionService service) where TRequest : ReturnResponse + { + try + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState.GetErrorMessages()); + } + + var validateResponse = service.PreQueueValidation(originalRequest); + if (!validateResponse.IsSuccessful) + { + return BadRequest($"Request is invalid. Response: {String.Join("\n ", validateResponse.MessageStack.ToArray())}"); + } + + await _queueFileOperationsService.QueueFileOperations(originalRequest, operationType); + return Ok(); + } + catch (ServiceException) + { + return StatusCode(StatusCodes.Status500InternalServerError); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/FileValidationController.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/FileValidationController.cs new file mode 100644 index 0000000..466db54 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/FileValidationController.cs @@ -0,0 +1,141 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Serilog; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Api.Internal.FileOperations.Services.FileActionServices; +using System.Collections.Generic; +using System.Threading.Tasks; +using LCU.Api.Internal.FileOperations.Extensions; +using LCU.Library.Internal.Exceptions; +using LCU.Api.Internal.FileOperations.Services.QueueServices; +using LCU.Library.Internal.FileOperationsModels.Models.Requests; + +namespace LCU.Api.Internal.FileOperations.Controllers; + +[ApiController] +[Route("[controller]")] +public class FileValidationController : ControllerBase +{ + private readonly IQueueFileOperationsService _queueFileOperationsService; + private readonly IActionService _validateFileService; + private readonly IActionService _validateFileSizeInRangeService; + + public FileValidationController(IQueueFileOperationsService fileOperationsService, + IActionService validateFileService, + IActionService validateFileSizeInRangeService + ) + { + _queueFileOperationsService = fileOperationsService; + _validateFileService = validateFileService; + _validateFileSizeInRangeService = validateFileSizeInRangeService; + } + // POST ValidateFileExists + [HttpPost("ValidateFileExists")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(bool), 400)] + [ProducesResponseType(500)] + public IActionResult ValidateFileExists([FromBody] string sourceFilePath) + { + try + { + var requestResponse = new RequestResponse(); + requestResponse = FileValidation.ValidateFileExists(sourceFilePath, requestResponse); + return Ok(requestResponse.IsSuccessful); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + // POST api/QueueValidateFile + [HttpPost("QueueValidateFile")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task ValidateFile([FromBody] ValidateFileRequest request) + { + try + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState.GetErrorMessages()); + } + + var validateResponse = _validateFileService.PreQueueValidation(request); + if (!validateResponse.IsSuccessful) + { + return BadRequest($"Request is invalid. Response: {String.Join("\n ", validateResponse.MessageStack.ToArray())}"); + } + + await _queueFileOperationsService.QueueFileOperations(request, FileOperationType.Validate); + return Accepted(); + } + catch (ServiceException) + { + return StatusCode(StatusCodes.Status500InternalServerError); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("ValidatePdfPageExists/{path}/{pageNumber:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(bool), 400)] + [ProducesResponseType(500)] + public IActionResult ValidatePdfPageExists(string path, int pageNumber) + { + try + { + var requestResponse = new RequestResponse(); + requestResponse = PdfValidations.ValidatePdfPageExists(requestResponse, path, pageNumber); + return Ok(requestResponse); + } + catch (Exception exception) + { + + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + // POST api/QueueValidateFileSizeInRange + [HttpPost("QueueValidateFileSizeInRange")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(List), 400)] + [ProducesResponseType(500)] + public async Task ValidateFileSizeInRange([FromBody] ValidateFileSizeInRangeRequest request) + { + try + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState.GetErrorMessages()); + } + + var validateResponse = _validateFileSizeInRangeService.PreQueueValidation(request); + if (!validateResponse.IsSuccessful) + { + return BadRequest($"Request is invalid. Response: {string.Join("\n ", validateResponse.MessageStack.ToArray())}"); + } + + await _queueFileOperationsService.QueueFileOperations(request, FileOperationType.ValidateFileSizeInRange); + return Accepted(); + } + catch (ServiceException) + { + return StatusCode(StatusCodes.Status500InternalServerError); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/IndexFileController.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/IndexFileController.cs new file mode 100644 index 0000000..e1b4f3b --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/IndexFileController.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading.Tasks; +using LCU.Api.Internal.FileOperations.Extensions; +using LCU.Api.Internal.FileOperations.Services.QueueServices; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.Exceptions; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Serilog; + +namespace LCU.Api.Internal.FileOperations.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class IndexFileController : ControllerBase +{ + private readonly IQueueFileOperationsService _queueFileOperationsService; + + public IndexFileController(IQueueFileOperationsService queueFileOperationsService) + { + _queueFileOperationsService = queueFileOperationsService; + } + + [HttpPost("QueueExtractIndexFileData")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ReturnResponse), 400)] + [ProducesResponseType(500)] + public async Task QueueExtractIndexFileData([FromBody] ExtractIndexFileDataRequest request) + { + try + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState.GetErrorMessages()); + } + + await _queueFileOperationsService.QueueFileOperations(request, FileOperationType.ExtractIndexFileData); + return Accepted(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/PdfController.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/PdfController.cs new file mode 100644 index 0000000..b4102f9 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/PdfController.cs @@ -0,0 +1,185 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Serilog; +using LCU.Api.Internal.FileOperations.Services; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.FileOperationsModels.Models.HtmlToPdfModels; +using LCU.Api.Internal.FileOperations.Extensions; +using LCU.Api.Internal.FileOperations.Services.QueueServices; +using LCU.Api.Internal.FileOperations.Services.PdfServices; +namespace LCU.Api.Internal.FileOperations.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class PdfController : ControllerBase +{ + private readonly IQueueFileOperationsService _queueFileOperationsService; + private readonly IPdfParserService _pdfParserService; + private readonly IBatchPdfParserService _batchPdfParserService; + private readonly IDirectFileService _directFileService; + private readonly IHtmlToPdfService _htmlToPdfService; + + public PdfController(IQueueFileOperationsService queueFileOperationsService, + IPdfParserService pdfParserService, + IBatchPdfParserService batchPdfParserService, + IDirectFileService directFileService, + IHtmlToPdfService htmlToPdfService) + { + _queueFileOperationsService = queueFileOperationsService; + _batchPdfParserService = batchPdfParserService; + _pdfParserService = pdfParserService; + _directFileService = directFileService; + _htmlToPdfService = htmlToPdfService; + } + + [HttpPost("QueueBatchPdfParser")] + [ProducesResponseType(202)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task QueueBatchPdfParser([FromBody] BatchParsePdfRequest request) + { + try + { + var requestResponse = _batchPdfParserService.IsRequestValid(request, new RequestResponse()); + if (!requestResponse.IsSuccessful) + { + return BadRequest($"Request is invalid. Response: {String.Join("\n ", requestResponse.MessageStack.ToArray())}"); + } + + await _queueFileOperationsService.QueueFileOperations(request, FileOperationType.BatchPdfParser); + return Accepted(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + /// + /// This will parse a PDF from a starting page to and a stopping page, save new pdf to RootPath, + /// and returns the PDF name. The PDF name will be set to ParsedPdfname or the default "{pdfParserRequest.PdfName}.{pdfParserRequest.StartingPage}.pdf". + /// + /// Notes: + /// - Page numbers range from 1-x. + /// - If PDF exists it will automatically overwrite. + /// - Original PDF can be open at the time of parsing. + /// + /// + /// + [HttpPost("ParsePdf")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(ParsePdfRequest), 400)] + [ProducesResponseType(500)] + public IActionResult ParsePdf([FromBody] ParsePdfRequest pdfParserRequest) + { + try + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState.GetErrorMessages()); + } + + var result = _pdfParserService.ParsePdf(pdfParserRequest); + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpGet("GetPdfContent/{pdfPath}")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public IActionResult GetPdfContent([FromRoute] string pdfPath) + { + if (string.IsNullOrWhiteSpace(pdfPath)) + { + return BadRequest("PDF Path is required"); + } + + try + { + var result = _directFileService.GetFileBytes(pdfPath); + return new FileContentResult(result, "application/pdf"); + } + catch (ArgumentException ae) + { + Log.Error(ae, $"Request for bad PDF path {pdfPath}"); + return BadRequest("PDF path not found"); + } + catch (Exception ex) + { + Log.Error(ex, $"Unexpected error retrieving PDF path {pdfPath}"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + /// + /// ConvertHmtlToPdf will take the HtmlToPdfRequest and generate a HTML file filling in the tags with the supplied data. + /// It will then utilize Chrome functionality to print a pdf and save it to a location. Finally converting to byte array and returning. + /// + /// HtmlToPdfRequest: + /// - HtmlTemplateFilePath: (REQUIRED) File Path of the main html template that the HTML file will start to build. + /// - PdfName: (REQUIRED) What the PDF and HTML files will be called. + /// - KeepHtmlFile: (Defaults = False) Mostly for testing purposes. If set to true it will keep the generated HTML file. + /// - KeepPdfFile: (Defaults = False) Mostly for testing purposes. If set to true it will keep the generated PDF file + /// - HtmlSaveFilePath: (REQUIRED) Location that the generated HTML file will be temporarily (KeepHtmlFile = false) stored. + /// - PdfSaveFilePath: (REQUIRED) Location that the generated PDF file will be temporarily (KeepHtmlFile = false) stored. + /// - BodyTagValues: Tags that will be filled in with supplied values. + /// - TagValues: + /// - Tag: (REQUIRED) Name of the tag in the HTML. In HTML should be surrounded by {}. This tag does not need {}. + /// - Value: (REQUIRED) Value in which will replace the {TagName}. + /// - BodyValueCollections: List of ValueCollections that give us the ability to replace a tag that is associated with a HTML Template. The ValucollectionItems is + /// looped through to generate what will replace the tag. Common use case would be a list of items or a table of items. + /// - CollectionTag: (REQUIRED) Name of the tag in the HTML. In HTML should be surrounded by {}. This tag does not need {}. + /// - CollectionHtmlTemplateFilePath: (REQUIRED) File Path of the collections html template that the HTML file will start to build. + /// - ValueCollectionItems: List of ValueCollectionItems that can be looped through to help build out what the CollectionTag will be replaced with. + /// ValueCollection has a nested ValueCollectionItems, giving the ability to create infinite amount of nested collections within collections. + /// - TagValues: List of TagValues + /// - ValueCollections: List of ValueCollections + /// + /// Notes: + /// - In HTML templates tags should be surrounded by braces {TagName}. The request does not need the braces {}. + /// - Any unused tags surrounded by braces will be removed at the end of the process. + /// - Can use \\\\zeus\DataIntegrationsQA\\PDFCreation\\ folder to store templates, generated HTML files, and generated PDF + /// - \\\\zeus\DataIntegrationsQA\\PDFCreation\\Html_Templates + /// - \\\\zeus\DataIntegrationsQA\\PDFCreation\\Html_Drop + /// - \\\\zeus\DataIntegrationsQA\\PDFCreation\\PDF_Drop + /// + [HttpPost("ConvertHtmlToPdf")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public IActionResult ConvertHtmlToPdf([FromBody] HtmlToPdfRequest htmlToPdfRequest) + { + try + { + var result = _htmlToPdfService.ConvertHtmlToPdf(htmlToPdfRequest); + return new FileContentResult(result, "application/pdf"); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/StatusController.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/StatusController.cs new file mode 100644 index 0000000..9ca9026 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Controllers/StatusController.cs @@ -0,0 +1,39 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http; +using Serilog; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.Status.Services; +namespace LCU.Api.Internal.FileOperations.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class StatusController : ControllerBase +{ + private readonly IApiHealthCheckService _apiHealthCheckService; + + public StatusController(IApiHealthCheckService apiHealthCheckService) + { + _apiHealthCheckService = apiHealthCheckService; + } + + [HttpGet] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public IActionResult Get() + { + try + { + return Ok(_apiHealthCheckService.CheckApiHealthAndBuildEnvironmentView()); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Extensions/ModelStateMessages.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Extensions/ModelStateMessages.cs new file mode 100644 index 0000000..65a0e60 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Extensions/ModelStateMessages.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace LCU.Api.Internal.FileOperations.Extensions +{ + public static class ModelStateMessages + { + public static List GetErrorMessages(this ModelStateDictionary dictionary) + { + return dictionary.SelectMany(m => m.Value.Errors) + .Select(m => m.ErrorMessage) + .ToList(); + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/LCU.Api.Internal.FileOperations.csproj b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/LCU.Api.Internal.FileOperations.csproj new file mode 100644 index 0000000..73b15af --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/LCU.Api.Internal.FileOperations.csproj @@ -0,0 +1,82 @@ + + + + net8.0 + InProcess + 3.0.0 + + 2.0.0 - Adding the use of DynamicNameSettings + 2.1.0 - Added CreateDirectory and RenameDirectory. Also the IActionService is taking over as the service interface for all the action services. + 2.2.0 - Addition of the RegexSettings for the Copy, Delete, and Move file operation. + 2.3.0 - Added NameSettings for RenameFile NewFileName field. + 2.4.0 - Fix to add failure validation for no files available for Moveing, Copying, and Deleteing with regex. + 2.5.0 - New BulkOperationsRequest and some service refactoring. + 2.6.0 - New UnzipFile functionality. + 2.7.0 - Dynamiz Zip and Unzip functionality + 2.8.0 - Add File Data Encryption/Decryption Actions. + 2.9.0 - Add SucceedOnError to Copy/Rename/Move/Delete File + 2.10.0 - Upgrade ApiResponse, change to await async QueueReturnResponse. + 2.11.0 - New UnzipPasswordProtectedFile functionality. + 2.12.0 - Upgrade to dotnet8. + 3.0.0 - Updated models project reference. + + + 2.0.0 - Adding the use of DynamicNameSettings + 2.1.0 - Added CreateDirectory and RenameDirectory. Also the IActionService is taking over as the service interface for all the action services. + 2.2.0 - Addition of the RegexSettings for the Copy, Delete, and Move file operation. + 2.3.0 - Added NameSettings for RenameFile NewFileName field. + 2.4.0 - Fix to add failure validation for no files available for Moveing, Copying, and Deleteing with regex. + 2.5.0 - New BulkOperationsRequest and some service refactoring. + 2.6.0 - New UnzipFile functionality. + 2.7.0 - Dynamiz Zip and Unzip functionality + 2.8.0 - Add File Data Encryption/Decryption Actions. + 2.9.0 - Add SucceedOnError to Copy/Rename/Move/Delete File + 2.10.0 - Upgrade ApiResponse, change to await async QueueReturnResponse. + 2.11.0 - New UnzipPasswordProtectedFile functionality. + 2.12.0 - Upgrade to dotnet8. + 3.0.0 - Updated models project reference. + + + + + C:\dev\projects\LCU.API.Internal.FileOperations\LCU.Api.Internal.FileOperations\LCU.Api.Internal.FileOperations.xml + 1701;1702;1591 + + + + C:\dev\projects\LCU.API.Internal.FileOperations\LCU.Api.Internal.FileOperations\LCU.Api.Internal.FileOperations.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/LCU.Api.Internal.FileOperations.xml b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/LCU.Api.Internal.FileOperations.xml new file mode 100644 index 0000000..04d51a7 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/LCU.Api.Internal.FileOperations.xml @@ -0,0 +1,42 @@ + + + + LCU.Api.Internal.FileOperations + + + + Gets a list of files based on regex substitution. + + Example: + POST /api/BuildDynamicFileList + { + "path": "\\\\172.16.13.17\\Reports\\", + "filename": "Action_IndirectVehicleLoanSales_date_time.csv", + "dynamicFileSettings": { + "regexSettings": [ + { + "tag": "date", + "regex": "\\d+_\\d+_\\d+" + }, + { + "tag": "time", + "regex": "\\d+" + }], + "daysFromToday": 0 + } + } + + + + + + + Bulk Operations was created to always try. This meaning that it should try to attempt + every operation action. This also means that it will alway try to also return response and + that it always returns true (Exeption being catastrophic exeption thrown). + + + + + + diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Models/IndexEvaluationRequest.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Models/IndexEvaluationRequest.cs new file mode 100644 index 0000000..0c3b192 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Models/IndexEvaluationRequest.cs @@ -0,0 +1,128 @@ +using System.ComponentModel.DataAnnotations; +using CsvHelper.Configuration.Attributes; +using LCU.Library.Internal.LcuEvaluationModels.Requests; + +namespace LCU.Api.Internal.FileOperations.Models; + +public class IndexEvaluationRequest : EvaluationRequest +{ + [Index(0)] + [Display(Name = "InputFieldZero")] + public string InputFieldZero { get; set; } + + [Index(1)] + [Display(Name = "InputFieldOne")] + public string InputFieldOne { get; set; } + + [Index(2)] + [Display(Name = "InputFieldTwo")] + public string InputFieldTwo { get; set; } + + [Index(3)] + [Display(Name = "InputFieldThree")] + public string InputFieldThree { get; set; } + + [Index(4)] + [Display(Name = "InputFieldFour")] + public string InputFieldFour { get; set; } + + [Index(5)] + [Display(Name = "InputFieldFive")] + public string InputFieldFive { get; set; } + + [Index(6)] + [Display(Name = "InputFieldSix")] + public string InputFieldSix { get; set; } + + [Index(7)] + [Display(Name = "InputFieldSeven")] + public string InputFieldSeven { get; set; } + + [Index(8)] + [Display(Name = "InputFieldEight")] + public string InputFieldEight { get; set; } + + [Index(9)] + [Display(Name = "InputFieldNine")] + public string InputFieldNine { get; set; } + + [Index(10)] + [Display(Name = "InputFieldTen")] + public string InputFieldTen { get; set; } + + [Index(11)] + [Display(Name = "InputFieldEleven")] + public string InputFieldEleven { get; set; } + + [Index(12)] + [Display(Name = "InputFieldTwelve")] + public string InputFieldTwelve { get; set; } + + [Index(13)] + [Display(Name = "InputFieldThirteen")] + public string InputFieldThirteen { get; set; } + + [Index(14)] + [Display(Name = "InputFieldFourteen")] + public string InputFieldFourteen { get; set; } + + [Index(15)] + [Display(Name = "InputFieldFifteen")] + public string InputFieldFifteen { get; set; } + + [Index(16)] + [Display(Name = "InputFieldSixteen")] + public string InputFieldSixteen { get; set; } + + [Index(17)] + [Display(Name = "InputFieldSeventeen")] + public string InputFieldSeventeen { get; set; } + + [Index(18)] + [Display(Name = "InputFieldEighteen")] + public string InputFieldEighteen { get; set; } + + [Index(19)] + [Display(Name = "InputFieldNineteen")] + public string InputFieldNineteen { get; set; } + + [Index(20)] + [Display(Name = "InputFieldTwenty")] + public string InputFieldTwenty { get; set; } + + [Index(21)] + [Display(Name = "InputFieldTwentyOne")] + public string InputFieldTwentyOne { get; set; } + + [Index(22)] + [Display(Name = "InputFieldTwentyTwo")] + public string InputFieldTwentyTwo { get; set; } + + [Index(23)] + [Display(Name = "InputFieldTwentyThree")] + public string InputFieldTwentyThree { get; set; } + + [Index(24)] + [Display(Name = "InputFieldTwentyFour")] + public string InputFieldTwentyFour { get; set; } + + [Index(25)] + [Display(Name = "InputFieldTwentyFive")] + public string InputFieldTwentyFive { get; set; } + + [Index(26)] + [Display(Name = "InputFieldTwentySix")] + public string InputFieldTwentySix { get; set; } + + [Index(27)] + [Display(Name = "InputFieldTwentySeven")] + public string InputFieldTwentySeven { get; set; } + + [Index(28)] + [Display(Name = "InputFieldTwentyEight")] + public string InputFieldTwentyEight { get; set; } + + [Index(29)] + [Display(Name = "InputFieldTwentyNine")] + public string InputFieldTwentyNine { get; set; } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Program.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Program.cs new file mode 100644 index 0000000..a6c351a --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Program.cs @@ -0,0 +1,227 @@ +using System; +using System.IO; +using System.Reflection; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; +using MassTransit; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LcuEvaluation; +using LCU.Library.Internal.LcuMassTransit; +using LCU.Library.Internal.LcuMassTransitModels; +using LCU.Library.Internal.Status; +using LCU.Library.Internal.Configuration; +using LCU.Library.Internal.FileOperationsModels.Models.Requests; +using LCU.Library.Internal.LCUServiceFactory; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Library.Internal.Logging.Models; +using LCU.Library.Internal.Logging; +using LCU.Api.Internal.FileOperations.Configurations; +using LCU.Api.Internal.FileOperations.Consumers; +using LCU.Api.Internal.FileOperations.Services; +using LCU.Api.Internal.FileOperations.Services.ExcelServices; +using LCU.Api.Internal.FileOperations.Services.FileActionServices; +using LCU.Api.Internal.FileOperations.Services.PdfServices; +using LCU.Api.Internal.FileOperations.Services.QueueServices; + +string MyAllowSpecificOrigins = "_myAllowSpecificOrigins"; + +var builder = BuildBuilder(args); +BuildServicesAndConfigurations(builder); +BuildAppAndRun(builder); + +WebApplicationBuilder BuildBuilder(string[] args) +{ + var builder = WebApplication.CreateBuilder(args); + builder.Host.UseSerilog(); + return builder; +} + +void BuildServicesAndConfigurations(WebApplicationBuilder builder) +{ + var services = builder.Services; + var configuration = builder.Configuration; + + //Configurations + var apiConfiguration = configuration.Bind(services, "Primary"); + var servicesFactoryConfiguration = configuration.Bind(services); + services.AddSingleton(apiConfiguration); + var loggerConfiguration = new LoggingConfiguration(apiConfiguration.AppName, + apiConfiguration.SeqApiKey, + apiConfiguration.SeqUrl); + var lcuLogging = new LcuLogging(loggerConfiguration); + lcuLogging.UseLcuLogging(); + + var connectionConfiguration = MassTransitConfigurationSetUp.AddConnectionConfiguration(ref services, configuration); + var receiveEndpointConfiguration = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, "FileOperationsReceiveEndpoint"); + services.AddSingleton(receiveEndpointConfiguration); + AddMassTransitServices(ref services, connectionConfiguration, receiveEndpointConfiguration); + + //Services + services.AddLCUStatusServicesWithApiHealthCheck(configuration); + services.AddLcuEvaluationServices(); + AddFileOperationServices(services); + AddFileActionServices(services); + AddServiceFactoryServices(services); + AddPdfServices(services); + AddExcelServices(services); + + services.AddLCUServiceFactoryAndValidateServiceRegistration(servicesFactoryConfiguration); + services.AddControllers(); + services.AddCors(options => + { + options.AddPolicy(MyAllowSpecificOrigins, + builder => + { + builder.WithOrigins( + apiConfiguration.CorsAllowedOrigins + ).AllowAnyHeader().AllowAnyMethod(); + }); + }); + + // Register the Swagger generator, defining 1 or more Swagger documents + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "File Operations API", Version = "v1" }); + // For this to work, I also had to check the generate xml box in build options. + var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.XML"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + c.IncludeXmlComments(xmlPath); + }); +} + +void BuildAppAndRun(WebApplicationBuilder builder) +{ + var app = builder.Build(); + + app.UseSwagger(); + + // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), + // specifying the Swagger JSON endpoint. + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "File Operations API"); + }); + + if (app.Environment.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + } + + app.UseAuthentication(); + app.UseCors(MyAllowSpecificOrigins); + app.UseHttpsRedirection(); + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + } + ); + + app.Run(); +} + +void AddFileOperationServices(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddServiceFactoryServices(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped>(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddFileActionServices(IServiceCollection services) +{ + services.AddScoped, MoveFileService>(); + services.AddScoped, ModifyFileService>(); + services.AddScoped, CopyFileService>(); + services.AddScoped, DeleteFileService>(); + services.AddScoped, RenameFileService>(); + services.AddScoped, ZipDirectoryService>(); + services.AddScoped, UnzipFileService>(); + services.AddScoped, UnzipPasswordProtectedFileService>(); + services.AddScoped, ValidateFileService>(); + services.AddScoped, CreateDirectoryService>(); + services.AddScoped, RenameDirectoryService>(); + services.AddScoped>, BulkOperationService>(); + services.AddScoped, EncryptFileDataService>(); + services.AddScoped, DecryptFileDataService>(); + services.AddScoped, WriteFileService>(); + services.AddScoped, DeleteDirectoryService>(); + services.AddScoped, AppendFileService>(); + services.AddScoped, ExtractIndexFileDataService>(); + services.AddScoped, DeleteEmptyDirectoryService>(); + services.AddScoped, ValidateFileSizeInRangeService>(); +} + +void AddPdfServices(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddExcelServices(IServiceCollection services) +{ + services.AddScoped(); +} + +void AddMassTransitServices(ref IServiceCollection services, + ConnectionConfiguration connectionConfiguration, + ReceiveEndpointConfiguration receiveEndpointConfiguration) +{ + services.AddMassTransit(x => + { + x.AddConsumer(); + + x.UsingRabbitMq((context, cfg) => + { + cfg.Host(connectionConfiguration.HostUrl, "/", host => + { + host.Username(connectionConfiguration.UserName); + host.Password(connectionConfiguration.Password); + }); + + cfg.ReceiveEndpoint(receiveEndpointConfiguration.ReceiveEndpoint, e => + { + e.ConfigureConsumer(context); + e.SetQuorumQueue(); + e.PrefetchCount = receiveEndpointConfiguration.PrefetchLimit; + }); + + }); + }); + services.AddMassTransitHostedService(); +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/CryptoHelper.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/CryptoHelper.cs new file mode 100644 index 0000000..5a7e43e --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/CryptoHelper.cs @@ -0,0 +1,213 @@ +using Org.BouncyCastle.Bcpg; +using Org.BouncyCastle.Bcpg.OpenPgp; +using Org.BouncyCastle.Security; +using System; +using System.IO; +using System.Text; +using Org.BouncyCastle.Utilities.IO; + +namespace LCU.Api.Internal.FileOperations.Services +{ + public static class CryptoHelper + { + private static PgpPublicKey ReadPublicKey(Stream inputStream) + { + inputStream = PgpUtilities.GetDecoderStream(inputStream); + PgpPublicKeyRingBundle pgpPub = new PgpPublicKeyRingBundle(inputStream); + + foreach (PgpPublicKeyRing keyRing in pgpPub.GetKeyRings()) + { + foreach (PgpPublicKey key in keyRing.GetPublicKeys()) + { + if (key.IsEncryptionKey) + { + return key; + } + } + } + + throw new ArgumentException("Can't find encryption key in key ring."); + } + + private static PgpPrivateKey FindSecretKey(PgpSecretKeyRingBundle pgpSec, long keyId, char[] pass) + { + PgpSecretKey pgpSecKey = pgpSec.GetSecretKey(keyId); + return pgpSecKey?.ExtractPrivateKey(pass); + } + + public static void DecryptPgpFile(string inputFile, string outputFile, string keyFile, string passPhrase) + { + var encryptedFileData = File.ReadAllBytes(inputFile); + var secretKeyData = File.ReadAllBytes(keyFile); + var decryptedData = DecryptPgpData(encryptedFileData, secretKeyData, passPhrase); + + File.WriteAllText(outputFile, decryptedData); + } + + public static string DecryptPgpData(byte[] inputData, byte[] secretKeyData, string passPhrase) + { + Stream inputStream = new MemoryStream(inputData); + Stream secretKeyStream = new MemoryStream(secretKeyData); + + string output; + + PgpObjectFactory pgpFactory = new PgpObjectFactory(PgpUtilities.GetDecoderStream(inputStream)); + // find secret key + PgpSecretKeyRingBundle pgpKeyRing = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(secretKeyStream)); + + PgpObject pgp = null; + if (pgpFactory != null) + { + pgp = pgpFactory.NextPgpObject(); + } + + // the first object might be a PGP marker packet. + PgpEncryptedDataList encryptedData = null; + if (pgp is PgpEncryptedDataList) + { + encryptedData = (PgpEncryptedDataList)pgp; + } + else + { + encryptedData = (PgpEncryptedDataList)pgpFactory.NextPgpObject(); + } + + // decrypt + PgpPrivateKey privateKey = null; + PgpPublicKeyEncryptedData pubKeyData = null; + foreach (PgpPublicKeyEncryptedData pubKeyDataItem in encryptedData.GetEncryptedDataObjects()) + { + privateKey = FindSecretKey(pgpKeyRing, pubKeyDataItem.KeyId, passPhrase.ToCharArray()); + + if (privateKey != null) + { + pubKeyData = pubKeyDataItem; + break; + } + } + + if (privateKey == null) + { + throw new ArgumentException("Secret key for message not found."); + } + + PgpObjectFactory plainFact = null; + using (Stream clear = pubKeyData.GetDataStream(privateKey)) + { + plainFact = new PgpObjectFactory(clear); + } + + PgpObject message = plainFact.NextPgpObject(); + + switch (message) + { + case PgpCompressedData data: + { + PgpCompressedData compressedData = data; + PgpObjectFactory pgpCompressedFactory = null; + + using (Stream compDataIn = compressedData.GetDataStream()) + { + pgpCompressedFactory = new PgpObjectFactory(compDataIn); + } + + message = pgpCompressedFactory.NextPgpObject(); + PgpLiteralData literalData = null; + if (message is PgpOnePassSignatureList) + { + message = pgpCompressedFactory.NextPgpObject(); + } + + literalData = (PgpLiteralData)message; + using (Stream unc = literalData.GetInputStream()) + { + output = GetString(unc); + } + + break; + } + case PgpLiteralData data: + { + PgpLiteralData literalData = data; + using (Stream unc = literalData.GetInputStream()) + { + output = GetString(unc); + } + + break; + } + case PgpOnePassSignatureList _: + throw new PgpException("Encrypted message contains a signed message - not literal data."); + default: + throw new PgpException("Message is not a simple encrypted file - type unknown."); + } + + return output; + } + + public static void EncryptPgpFile(string inputFile, string outputFile, string publicKeyFile, bool applyArmor, bool withIntegrityCheck) + { + using (Stream publicKeyStream = File.OpenRead(publicKeyFile)) + { + PgpPublicKey pubKey = ReadPublicKey(publicKeyStream); + + using (MemoryStream outputBytes = new MemoryStream()) + { + PgpCompressedDataGenerator dataCompressor = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip); + PgpUtilities.WriteFileToLiteralData(dataCompressor.Open(outputBytes), PgpLiteralData.Binary, new FileInfo(inputFile)); + + dataCompressor.Close(); + PgpEncryptedDataGenerator dataGenerator = + new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom()); + + dataGenerator.AddMethod(pubKey); + byte[] dataBytes = outputBytes.ToArray(); + + using (Stream outputStream = File.Create(outputFile)) + { + if (applyArmor) + { + using (ArmoredOutputStream armoredStream = new ArmoredOutputStream(outputStream)) + { + WriteStream(dataGenerator.Open(armoredStream, dataBytes.Length), ref dataBytes); + } + } + else + { + WriteStream(dataGenerator.Open(outputStream, dataBytes.Length), ref dataBytes); + } + } + } + } + } + + public static void WriteStream(Stream inputStream, ref byte[] dataBytes) + { + using (Stream outputStream = inputStream) + { + outputStream.Write(dataBytes, 0, dataBytes.Length); + } + } + + public static string GetString(Stream inputStream) + { + string output; + using (StreamReader reader = new StreamReader(inputStream)) + { + output = reader.ReadToEnd(); + } + + return output; + } + + public static Stream GetStream(string stringData) + { + MemoryStream stream = new MemoryStream(); + StreamWriter writer = new StreamWriter(stream); + writer.Write(stringData); + writer.Flush(); + stream.Position = 0; + return stream; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DirectFileOperationsService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DirectFileOperationsService.cs new file mode 100644 index 0000000..c7db7ff --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DirectFileOperationsService.cs @@ -0,0 +1,46 @@ +using System; +using Newtonsoft.Json; +using Serilog; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.Exceptions; +using System.Threading.Tasks; +using LCU.Library.Internal.ApiResponse.Services; +using LCU.Library.Internal.ApiResponse.Models; + +namespace LCU.Api.Internal.FileOperations.Services; + +public class DirectFileOperationsService : IDirectFileOperationsService +{ + private readonly IFileOperationsService _fileOperationsService; + + public DirectFileOperationsService(IFileOperationsService fileOperationsService) + { + _fileOperationsService = fileOperationsService; + } + + public async Task HandleDirectFileOperationsRequest(TRequest request, + FileOperationType fileOperationType) + where TRequest : ReturnResponse + { + try + { + var queueRequest = new QueueRequest + { + Request = JsonConvert.SerializeObject(request), + FileOperationType = fileOperationType, + Attempts = 0 + }; + + var requestResponse = _fileOperationsService.PerformFileOperation(queueRequest); + await QueueReturnResponse.QueueResponse(request, requestResponse.IsSuccessful, requestResponse.MessageStack); + + return requestResponse; + } + catch (Exception exception) + { + var message = "Failure to Handle Direct File Operations Request"; + Log.Error(exception, message); + throw new ServiceException($"{message}, Exception Message: {exception.Message}"); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DirectFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DirectFileService.cs new file mode 100644 index 0000000..23564cc --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DirectFileService.cs @@ -0,0 +1,18 @@ +using System; +using System.IO; + +namespace LCU.Api.Internal.FileOperations.Services +{ + public class DirectFileService : IDirectFileService + { + public byte[] GetFileBytes(string path) + { + if (!string.IsNullOrWhiteSpace(path) && File.Exists(path)) + { + return File.ReadAllBytes(path); + } + + throw new ArgumentException($"Path {path} must exist."); + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DynamicDirectoryService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DynamicDirectoryService.cs new file mode 100644 index 0000000..ca6b388 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/DynamicDirectoryService.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Serilog; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Library.Internal.Exceptions; +namespace LCU.Api.Internal.FileOperations.Services; + +public class DynamicDirectoryService : IDynamicDirectoryService +{ + public IEnumerable BuildDirectoryList(DynamicDirectoryListRequest request) + { + try + { + var dynamicDirectoryNameWithDates = DynamicNameBuilder.BuildDynamicName(request.DynamicDirectoryName, request.DynamicFileSettings); + var directoryNameList = RegexNameHandler.BuildDirectoryList(request.Path, dynamicDirectoryNameWithDates, request.DynamicFileSettings); + return directoryNameList; + } + catch (Exception exception) + { + Log.Error(exception, $"Error while trying to build directory list. Request: {JsonConvert.SerializeObject(request)}"); + throw new ServiceException(exception.Message); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/ExcelServices/ExcelService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/ExcelServices/ExcelService.cs new file mode 100644 index 0000000..dd5834e --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/ExcelServices/ExcelService.cs @@ -0,0 +1,119 @@ +using System; +using System.IO; +using System.Linq; +using Serilog; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Spreadsheet; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Responses; +using LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Requests; + +namespace LCU.Api.Internal.FileOperations.Services.ExcelServices; + +public class ExcelService : IExcelService +{ + public ExcelDataResponse ExtractExcelWorkBookData(ExcelDataRequest excelDataRequest) + { + var excelDataResponse = new ExcelDataResponse(); + try + { + var fileBytes = Convert.FromBase64String(excelDataRequest.FileContent); + using var memoryStream = new MemoryStream(fileBytes); + + using SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(memoryStream, false); + WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart; + Workbook workbook = workbookPart.Workbook; + foreach (Sheet sheet in workbook.GetFirstChild().Elements()) + { + SheetRequest sheetRequest = null; + if (excelDataRequest.SkipSheetForExtraction(sheet.Name, out sheetRequest)) + { + continue; + } + + var sheetResponse = new SheetResponse(sheet.Name); + var worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id); + var sheetData = worksheetPart.Worksheet.Elements().FirstOrDefault(); + if (sheetData == null) + { + Log.Information($"Sheet named {sheet.Name} does not have any sheet data."); + continue; + } + + sheetResponse = ExtractRowData(sheetData, sheetRequest, sheetResponse); + excelDataResponse.WorkBook.SheetResponses.Add(sheetResponse); + } + + return excelDataResponse; + } + catch (Exception exception) + { + var message = $"Failure during ExtractExcelWorkBookData. Exception Message: {exception.Message}"; + Log.Error(message, exception); + throw new ServiceException(message); + } + } + + private SheetResponse ExtractRowData(SheetData sheetData, SheetRequest sheetRequest, SheetResponse sheetResponse) + { + foreach (var row in sheetData.Elements()) + { + if (!int.TryParse(row.RowIndex, out int rowIndex)) + { + continue; + } + + if (sheetRequest.SkipRowForExtraction(rowIndex)) + { + continue; + } + + sheetResponse = ExtractCellData(row, rowIndex, sheetRequest, sheetResponse); + } + + return sheetResponse; + } + + private SheetResponse ExtractCellData(Row row, int rowIndex, SheetRequest sheetRequest, SheetResponse sheetResponse) + { + foreach (var cell in row.Elements()) + { + var columnName = GetColumnName(cell.CellReference); + if (sheetRequest.SkipColumnForExtraction(columnName)) + { + continue; + } + + if(cell is null || cell.CellValue is null) + { + continue; + } + + var cellResponse = new CellResponse() + { + Row = rowIndex, + ColumnName = columnName, + Value = cell.CellValue.InnerText, + Formula = cell.CellFormula is null ? string.Empty : cell.CellFormula.InnerText + }; + sheetResponse.CellResponses.Add(cellResponse); + } + + return sheetResponse; + } + + private static string GetColumnName(string cellReference) + { + string columnName = ""; + foreach (char character in cellReference) + { + if (!char.IsLetter(character)) + { + break; + } + + columnName += character; + } + return columnName; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/ExcelServices/IExcelService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/ExcelServices/IExcelService.cs new file mode 100644 index 0000000..ad01d5f --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/ExcelServices/IExcelService.cs @@ -0,0 +1,9 @@ +using LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Requests; +using LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Responses; + +namespace LCU.Api.Internal.FileOperations.Services.ExcelServices; + +public interface IExcelService +{ + ExcelDataResponse ExtractExcelWorkBookData(ExcelDataRequest excelDataRequest); +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/AppendFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/AppendFileService.cs new file mode 100644 index 0000000..b512ca9 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/AppendFileService.cs @@ -0,0 +1,72 @@ +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using Newtonsoft.Json; +using System; +using System.IO; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices +{ + public class AppendFileService : FileActionService, IFileActionService, IActionService + { + public AppendFileService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + + try + { + var fileRequest = JsonConvert.DeserializeObject(queueRequest.Request); + + requestResponse = PerformRequest(fileRequest); + + requestResponse.ReturnResponse = fileRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(AppendFileRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, AppendFile); + } + private void AppendFile(AppendFileRequest request) + { + try + { + File.AppendAllText(request.FullFilePath, request.FileData); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(AppendFileRequest originalRequest) + { + var requestResponse = new RequestResponse(); + + requestResponse = IsRequestValid(originalRequest, requestResponse); + + return requestResponse; + } + + public RequestResponse IsRequestValid(AppendFileRequest originalRequest, RequestResponse requestResponse) + { + requestResponse = DirectoryValidation.ValidateDirectoryExists(originalRequest.DestinationPath, requestResponse); + + return requestResponse; + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/BulkOperationService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/BulkOperationService.cs new file mode 100644 index 0000000..fcc8198 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/BulkOperationService.cs @@ -0,0 +1,85 @@ +using System; +using System.Linq; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class BulkOperationService : FileActionService, IFileActionService, IActionService> +{ + private readonly IActionService _actionService; + + public BulkOperationService(ServicesFactoryConfiguration servicesFactoryConfiguration, IActionService actionService) + : base(servicesFactoryConfiguration) + { + _actionService = actionService; + } + + /// + /// Bulk Operations was created to always try. This meaning that it should try to attempt + /// every operation action. This also means that it will alway try to also return response and + /// that it always returns true (Exeption being catastrophic exeption thrown). + /// + /// + /// + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var request = JsonConvert.DeserializeObject>(queueRequest.Request); + requestResponse = PerformRequest(request); + + //Bulk Operations just tries so if it gets to this point its sucessful. + requestResponse.IsSuccessful = true; + requestResponse.ReturnResponse = request; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(BulkOperationRequest request) + { + return BulkOperation(request); + } + + private RequestResponse BulkOperation(BulkOperationRequest request) + { + var finalRequestResponse = new RequestResponse(); + foreach (var operationRequest in request.OperationRequests) + { + var requestResponse = _actionService.PerformRequest(operationRequest); + if (!requestResponse.IsSuccessful) + { + finalRequestResponse.IsSuccessful = requestResponse.IsSuccessful; + finalRequestResponse.MessageStack.AddRange(requestResponse.MessageStack); + } + } + + return finalRequestResponse; + } + + public RequestResponse PreQueueValidation(BulkOperationRequest originalRequest) + { + var requestResponse = new RequestResponse(); + requestResponse = IsRequestValid(originalRequest, requestResponse); + return requestResponse; + } + + public RequestResponse IsRequestValid(BulkOperationRequest request, RequestResponse requestResponse) + { + var anyOperationRequests = request.OperationRequests.Any(); + if (!anyOperationRequests) + { + requestResponse.IsSuccessful = anyOperationRequests; + requestResponse.MessageStack.Add($"There are no bulk operation requests to perform."); + } + + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/CopyFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/CopyFileService.cs new file mode 100644 index 0000000..6165f47 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/CopyFileService.cs @@ -0,0 +1,104 @@ +using System; +using System.IO; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Api.Internal.FileOperations.Validations; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class CopyFileService : FileActionService, IFileActionService, IActionService +{ + public CopyFileService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var fileRequest = JsonConvert.DeserializeObject(queueRequest.Request); + var requests = DynamicNameBuilder.BuildListOfRequest(fileRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requests, fileRequest.Source, fileRequest.SucceedOnError, requestResponse); + + foreach (var request in requests) + { + var temporaryRequestResponse = PerformRequest(request); + if (!temporaryRequestResponse.IsSuccessful) + { + requestResponse.IsSuccessful = fileRequest.SucceedOnError; + if (fileRequest.SucceedOnError) + { + requestResponse.MessageStack.Add("Succeed On Error is set."); + } + + requestResponse.MessageStack.AddRange(temporaryRequestResponse.MessageStack); + } + } + + requestResponse.ReturnResponse = fileRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(CopyFileRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, CopyFile); + } + + private void CopyFile(CopyFileRequest request) + { + try + { + File.Copy(request.Source, request.Destination, request.Overwrite); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(CopyFileRequest originalRequest) + { + var requestResponse = new RequestResponse(); + var requestList = DynamicNameBuilder.BuildListOfRequest(originalRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requestList, originalRequest.SourcePath, originalRequest.SucceedOnError, requestResponse); + foreach (var request in requestList) + { + requestResponse = IsRequestValid(request, requestResponse); + } + + return requestResponse; + } + + + public RequestResponse IsRequestValid(CopyFileRequest fileRequest, RequestResponse requestResponse) + { + if (fileRequest.SucceedOnError) + { + return requestResponse; + } + requestResponse = FileValidation.ValidateSourceNotEmpty(fileRequest.Source, requestResponse); + requestResponse = FileValidation.ValidateFileExists(fileRequest.Source, requestResponse); + + requestResponse = FileValidation.ValidateDestinationNotEmpty(fileRequest.Destination, requestResponse); + requestResponse = FileValidation.ValidateDirectoryExists(fileRequest.DestinationPath, requestResponse); + + if (!fileRequest.Overwrite) + { + requestResponse = FileValidation.ValidateFileNotExists(fileRequest.Destination, requestResponse); + } + + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/CreateDirectoryService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/CreateDirectoryService.cs new file mode 100644 index 0000000..725fdc5 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/CreateDirectoryService.cs @@ -0,0 +1,66 @@ +using System; +using System.IO; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Api.Internal.FileOperations.Validations; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class CreateDirectoryService : FileActionService, IFileActionService, IActionService +{ + public CreateDirectoryService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var directoryRequest = JsonConvert.DeserializeObject(queueRequest.Request); + requestResponse = PerformRequest(directoryRequest); + requestResponse.ReturnResponse = directoryRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(CreateDirectoryRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, CreateDirectory); + } + + private void CreateDirectory(CreateDirectoryRequest request) + { + try + { + Directory.CreateDirectory(request.DirectoryDestination); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(CreateDirectoryRequest originalRequest) + { + var requestResponse = new RequestResponse(); + requestResponse = IsRequestValid(originalRequest, requestResponse); + return requestResponse; + } + + public RequestResponse IsRequestValid(CreateDirectoryRequest request, RequestResponse requestResponse) + { + requestResponse = FileValidation.ValidateDirectoryExists(request.DirectoryPath, requestResponse); + requestResponse = FileValidation.ValidateDirectoryNotExists(request.DirectoryDestination, requestResponse); + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DecryptFileDataService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DecryptFileDataService.cs new file mode 100644 index 0000000..bebdde7 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DecryptFileDataService.cs @@ -0,0 +1,68 @@ +using System; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Api.Internal.FileOperations.Validations; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class DecryptFileDataService : FileActionService, IFileActionService, IActionService +{ + public DecryptFileDataService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var request = JsonConvert.DeserializeObject(queueRequest.Request); + requestResponse = PerformRequest(request); + requestResponse.ReturnResponse = request; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(DecryptFileDataRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, ExecuteRequestAction); + } + + private void ExecuteRequestAction(DecryptFileDataRequest request) + { + try + { + CryptoHelper.DecryptPgpFile(request.Source, request.Destination, request.KeySource, request.PassPhrase); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(DecryptFileDataRequest originalRequest) + { + var requestResponse = new RequestResponse(); + requestResponse = IsRequestValid(originalRequest, requestResponse); + return requestResponse; + } + + public RequestResponse IsRequestValid(DecryptFileDataRequest fileRequest, RequestResponse requestResponse) + { + requestResponse = FileValidation.ValidateDirectoryExists(fileRequest.SourcePath, requestResponse); + requestResponse = FileValidation.ValidateDirectoryExists(fileRequest.DestinationPath, requestResponse); + requestResponse = FileValidation.ValidateDirectoryExists(fileRequest.KeySourcePath, requestResponse); + requestResponse = FileValidation.ValidateFileExists(fileRequest.Source, requestResponse); + requestResponse = FileValidation.ValidateFileExists(fileRequest.KeySource, requestResponse); + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteDirectoryService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteDirectoryService.cs new file mode 100644 index 0000000..823cc6c --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteDirectoryService.cs @@ -0,0 +1,123 @@ +using System; +using System.IO; +using System.Collections.Generic; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class DeleteDirectoryService : FileActionService, IFileActionService, IActionService +{ + public DeleteDirectoryService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var directoryRequest = JsonConvert.DeserializeObject(queueRequest.Request); + + var requests = BuildListOfRequest(directoryRequest); + requestResponse = DirectoryValidation.ValidateDirectoryRequestsWithinLimit(requests, + directoryRequest.MaxAmountOfDirectories, + requestResponse); + if (!requestResponse.IsSuccessful) + { + return requestResponse; + } + + foreach (var request in requests) + { + var temporaryRequestResponse = PerformRequest(request); + if (!temporaryRequestResponse.IsSuccessful) + { + requestResponse.IsSuccessful = temporaryRequestResponse.IsSuccessful; + requestResponse.MessageStack.AddRange(temporaryRequestResponse.MessageStack); + } + } + + requestResponse.ReturnResponse = directoryRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(DeleteDirectoryRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, DeleteDirectory); + } + + private void DeleteDirectory(DeleteDirectoryRequest request) + { + try + { + Directory.Delete(request.FinalDirectoryPath, request.Recursive); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(DeleteDirectoryRequest originalRequest) + { + var requestResponse = new RequestResponse(); + requestResponse = DirectoryValidation.ValidateDirectoryExists(originalRequest.DirectoryRootPath, requestResponse); + if (!requestResponse.IsSuccessful) + { + return requestResponse; + } + + var requestList = BuildListOfRequest(originalRequest); + requestResponse = DirectoryValidation.ValidateDirectoryRequestsWithinLimit(requestList, + originalRequest.MaxAmountOfDirectories, + requestResponse); + if (!requestResponse.IsSuccessful) + { + return requestResponse; + } + + foreach (var request in requestList) + { + requestResponse = IsRequestValid(request, requestResponse); + } + + return requestResponse; + } + + public List BuildListOfRequest(DeleteDirectoryRequest originalRequest) + { + var nameList = RegexNameHandler.BuildDirectoryList(originalRequest.DirectoryRootPath, + originalRequest.FinalDirectoryName, + originalRequest.DirectoryNameSettings); + var requests = new List(); + + foreach (var name in nameList) + { + var newRequest = Clone.DeepClone(originalRequest); + newRequest.DirectoryName = name; + + requests.Add(newRequest); + } + + return requests; + } + + public RequestResponse IsRequestValid(DeleteDirectoryRequest request, RequestResponse requestResponse) + { + requestResponse = DirectoryValidation.ValidateDirectoryExists(request.FinalDirectoryPath, requestResponse); + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteEmptyDirectoryService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteEmptyDirectoryService.cs new file mode 100644 index 0000000..f92746c --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteEmptyDirectoryService.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.IO; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using Newtonsoft.Json; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices +{ + public class DeleteEmptyDirectoryService : FileActionService, IFileActionService, IActionService + { + public DeleteEmptyDirectoryService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var directoryRequest = JsonConvert.DeserializeObject(queueRequest.Request); + + var requests = BuildListOfRequest(directoryRequest); + + foreach (var request in requests) + { + if (DirectoryValidation.ValidateDirectoryIsEmpty(request.FinalDirectoryPath, new RequestResponse()).IsSuccessful) + { + var temporaryRequestResponse = PerformRequest(request); + + if (!temporaryRequestResponse.IsSuccessful) + { + requestResponse.IsSuccessful = temporaryRequestResponse.IsSuccessful; + requestResponse.MessageStack.AddRange(temporaryRequestResponse.MessageStack); + } + } + else + { + requestResponse.MessageStack.Add($"Directory {request.FinalDirectoryPath} is skipped as it is not empty and won't be deleted."); + } + } + + requestResponse.ReturnResponse = directoryRequest; + return requestResponse; + } + catch (Exception exception) + { + + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(DeleteEmptyDirectoryRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, DeleteEmptyDirectory); + } + + private void DeleteEmptyDirectory(DeleteEmptyDirectoryRequest request) + { + try + { + Directory.Delete(request.FinalDirectoryPath); + + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(DeleteEmptyDirectoryRequest originalRequest) + { + var requestResponse = new RequestResponse(); + requestResponse = DirectoryValidation.ValidateDirectoryExists(originalRequest.SubDirectoryPath, requestResponse); + if (!requestResponse.IsSuccessful) + { + return requestResponse; + } + + var requestList = BuildListOfRequest(originalRequest); + + foreach (var request in requestList) + { + requestResponse = IsRequestValid(request, requestResponse); + } + + return requestResponse; + } + + public RequestResponse IsRequestValid(DeleteEmptyDirectoryRequest request, RequestResponse requestResponse) + { + requestResponse = DirectoryValidation.ValidateDirectoryExists(request.FinalDirectoryPath, requestResponse); + return requestResponse; + } + + private List BuildListOfRequest(DeleteEmptyDirectoryRequest originalRequest) + { + var nameList = RegexNameHandler.BuildDirectoryList(originalRequest.SubDirectoryPath, + originalRequest.FinalDirectoryName, + originalRequest.DirectoryNameSettings); + + var requests = new List(); + + foreach (var name in nameList) + { + var newRequest = Clone.DeepClone(originalRequest); + newRequest.DynamicDirectoryName = name; + + requests.Add(newRequest); + } + + return requests; + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteFileService.cs new file mode 100644 index 0000000..48a27ed --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/DeleteFileService.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Api.Internal.FileOperations.Validations; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class DeleteFileService : FileActionService, IFileActionService, IActionService +{ + public DeleteFileService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var fileRequest = JsonConvert.DeserializeObject(queueRequest.Request); + var requests = BuildListOfRequest(fileRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requests, fileRequest.Source, requestResponse); + + foreach (var request in requests) + { + var temporaryRequestResponse = PerformRequest(request); + if (!temporaryRequestResponse.IsSuccessful) + { + requestResponse.IsSuccessful = temporaryRequestResponse.IsSuccessful; + requestResponse.MessageStack.AddRange(temporaryRequestResponse.MessageStack); + } + } + + requestResponse.ReturnResponse = fileRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(DeleteFileRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, DeleteFile); + } + + private void DeleteFile(DeleteFileRequest request) + { + try + { + File.Delete(request.Source); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(DeleteFileRequest originalRequest) + { + var requestResponse = new RequestResponse(); + var requestList = BuildListOfRequest(originalRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requestList, originalRequest.SourcePath, requestResponse); + foreach (var request in requestList) + { + requestResponse = IsRequestValid(request, requestResponse); + } + + return requestResponse; + } + + public List BuildListOfRequest(DeleteFileRequest originalRequest) + { + var nameList = RegexNameHandler.BuildFileList(originalRequest.SourcePath, + originalRequest.FinalSourceFile, + originalRequest.FileNameSetting); + var requests = new List(); + + foreach (var name in nameList) + { + var newRequest = Clone.DeepClone(originalRequest); + newRequest.SourceFile = name; + + requests.Add(newRequest); + } + + return requests; + } + + public RequestResponse IsRequestValid(DeleteFileRequest fileRequest, RequestResponse requestResponse) + { + if (fileRequest.SucceedOnError) + { + return requestResponse; + } + + requestResponse = FileValidation.ValidateSourceNotEmpty(fileRequest.Source, requestResponse); + requestResponse = FileValidation.ValidateFileExists(fileRequest.Source, requestResponse); + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/EncryptFileDataService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/EncryptFileDataService.cs new file mode 100644 index 0000000..d230956 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/EncryptFileDataService.cs @@ -0,0 +1,68 @@ +using System; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Api.Internal.FileOperations.Validations; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class EncryptFileDataService : FileActionService, IFileActionService, IActionService +{ + public EncryptFileDataService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var request = JsonConvert.DeserializeObject(queueRequest.Request); + requestResponse = PerformRequest(request); + requestResponse.ReturnResponse = request; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(EncryptFileDataRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, ExecuteRequestAction); + } + + private void ExecuteRequestAction(EncryptFileDataRequest request) + { + try + { + CryptoHelper.EncryptPgpFile(request.Source, request.Destination, request.KeySource, true, true); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(EncryptFileDataRequest originalRequest) + { + var requestResponse = new RequestResponse(); + requestResponse = IsRequestValid(originalRequest, requestResponse); + return requestResponse; + } + + public RequestResponse IsRequestValid(EncryptFileDataRequest fileRequest, RequestResponse requestResponse) + { + requestResponse = FileValidation.ValidateDirectoryExists(fileRequest.SourcePath, requestResponse); + requestResponse = FileValidation.ValidateDirectoryExists(fileRequest.DestinationPath, requestResponse); + requestResponse = FileValidation.ValidateDirectoryExists(fileRequest.KeySourcePath, requestResponse); + requestResponse = FileValidation.ValidateFileExists(fileRequest.Source, requestResponse); + requestResponse = FileValidation.ValidateFileExists(fileRequest.KeySource, requestResponse); + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ExtractIndexFileDataService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ExtractIndexFileDataService.cs new file mode 100644 index 0000000..4c86500 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ExtractIndexFileDataService.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using CsvHelper; +using CsvHelper.Configuration; +using CsvHelper.Configuration.Attributes; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Api.Internal.FileOperations.Models; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Library.Internal.LcuEvaluation.Services.EvaluationServices; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Library.Internal.TypeExtentions.EnumExtensions; +using Newtonsoft.Json; +using Serilog; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class ExtractIndexFileDataService : FileActionService, IFileActionService, IActionService +{ + private readonly IRunRuleService _runRuleService; + private const string RegexMatch = "(\")(.*?)(\")"; + + public ExtractIndexFileDataService(IRunRuleService runRuleService, ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + _runRuleService = runRuleService; + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var request = JsonConvert.DeserializeObject(queueRequest.Request); + var requests = DynamicNameBuilder.BuildListOfRequest(request); + + if (requests.Count > 1) + { + requestResponse.MessageStack.Add("More than one file was found."); + requestResponse.IsSuccessful = false; + } + else + { + requestResponse = PerformRequest(requests.FirstOrDefault()); + } + + requestResponse.ReturnResponse = requests.FirstOrDefault(); + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(ExtractIndexFileDataRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, ExtractIndexFileData); + } + + public RequestResponse PreQueueValidation(ExtractIndexFileDataRequest originalRequest) + { + var requestResponse = new RequestResponse(); + return requestResponse; + } + + public RequestResponse IsRequestValid(ExtractIndexFileDataRequest request, RequestResponse requestResponse) + { + requestResponse = FileValidation.ValidateFileExists(request.FinalIndexFilePath, requestResponse); + requestResponse = DirectoryValidation.ValidateDirectoryExists(request.FinalDestinationPath, requestResponse); + + return requestResponse; + } + + private void ExtractIndexFileData(ExtractIndexFileDataRequest request) + { + try + { + var delimiter = request.DelimiterType.GetNameFromValue(); + + var fileLines = GetFileContents(request.FinalIndexFilePath); + + if (fileLines is null || !fileLines.Any()) + { + Log.Information($"No content was extracted from filepath: ({request.FinalIndexFilePath})"); + return; + } + + var recordsAreWrappedInQuotes = HasQuotes(fileLines); + var headers = request.IndexHasHeaders ? GetHeaders(fileLines, delimiter, recordsAreWrappedInQuotes) : new List(); + var columnMax = GetColumnCount(fileLines, delimiter, recordsAreWrappedInQuotes); + var fileContent = string.Join(Environment.NewLine, fileLines); + var indexEvaluationRequests = GetCsvRecords(fileContent, + delimiter, + request.IndexHasHeaders); + var evaluatedRequests = BuildAndRunRules(indexEvaluationRequests, request); + + var passedList = FilterColumns(evaluatedRequests.Passed, + request.IndexesToExtract, + columnMax, + request.ExtractAllIndexes); + var failedList = FilterColumns(evaluatedRequests.Failed, + request.IndexesToExtract, + columnMax, + true); + + if (passedList is null || !passedList.Any()) + { + Log.Information("No results passed evaluations to write to destination file."); + return; + } + + OutputResults(request, failedList, passedList, headers, recordsAreWrappedInQuotes); + } + catch (Exception exception) + { + throw new Exception(@$"Exception thrown while trying to ExecuteRequestAction. + ExceptionMessage: {exception.Message} + Request: {JsonConvert.SerializeObject(request)}"); + } + } + + private (List Passed, List Failed) BuildAndRunRules(List indexEvaluationRequests, ExtractIndexFileDataRequest request) + { + if (request.EvaluationRule is null) + { + return (indexEvaluationRequests, new List()); + } + + var passedRules = new List(); + var failedRules = new List(); + + foreach (var indexEvaluationRequest in indexEvaluationRequests) + { + if (_runRuleService.BuildAndRunRule(new List { indexEvaluationRequest }, request.EvaluationRule)) + { + passedRules.Add(indexEvaluationRequest); + } + else + { + failedRules.Add(indexEvaluationRequest); + } + } + + return (passedRules, failedRules); + } + + private static List GetFileContents(string filepath) + => File.ReadAllLines(filepath).Where(line => !string.IsNullOrWhiteSpace(line)).ToList(); + + private static bool HasQuotes(List content) + => Regex.Split(content.First(), RegexMatch).Length > 1; + + private static List GetHeaders(List content, string delimiter, bool recordsAreWrappedInQuotes) + => recordsAreWrappedInQuotes ? Regex.Split(content.First(), RegexMatch).Where(x => !new List { delimiter, "\"" }.Contains(x) + && !string.IsNullOrWhiteSpace(x)).ToList() + : content.First().Split(delimiter).ToList(); + + private static int GetColumnCount(List content, string delimiter, bool recordsAreWrappedInQuotes) + { + var line = content.First(); + + if (!recordsAreWrappedInQuotes) + { + return line.Split(delimiter).Length; + } + + var filterList = new List { string.Empty, "\"", delimiter }; + return Regex.Split(line, RegexMatch).Where(x => !filterList.Contains(x)).ToList().Count; + } + + private static List GetCsvRecords(string fileContent, string delimiter, bool hasHeaders) + { + using var stringReader = new StringReader(fileContent); + var config = new CsvConfiguration(CultureInfo.InvariantCulture) + { + Delimiter = delimiter, + MissingFieldFound = null, + HasHeaderRecord = hasHeaders, + BadDataFound = null + }; + + using var csvReader = new CsvReader(stringReader, config); + return csvReader.GetRecords().ToList(); + } + + private static List> FilterColumns(List records, + List indexesToExtract, + int columnMax, + bool extractAllIndexes) + { + if (!extractAllIndexes && (indexesToExtract is null || !indexesToExtract.Any())) + { + throw new Exception("IndexesToExtract was empty"); + } + + var keepProperties = new List(); + var removeProperties = new List(); + + var indexes = typeof(IndexEvaluationRequest).GetProperties().ToDictionary( + prop => ((IndexAttribute)prop.GetCustomAttributes(true).FirstOrDefault(attr => attr.GetType() == typeof(IndexAttribute)))?.Index ?? int.MaxValue, + prop => prop.Name); + + var recordsDictionary = JsonConvert.DeserializeObject>>(JsonConvert.SerializeObject(records)); + + keepProperties.AddRange(indexes.Where(index => extractAllIndexes ? index.Key < columnMax : indexesToExtract.Contains(index.Key)) + .Select(x => x.Value).Distinct()); + + return recordsDictionary.Select(recordList => + recordList.Where(record => + (!keepProperties.Any() || keepProperties.Contains(record.Key)) + && !removeProperties.Contains(record.Key) + ).Select(record => record.Value).ToList() + ).ToList(); + } + + private static string TransformToOutputString(IEnumerable> content, string delimiter, string wrapText) + => string.Join(Environment.NewLine, content.Select(x => string.Join(delimiter, x.Select(y => $"{wrapText}{y}{wrapText}")))); + + private static void OutputResults(ExtractIndexFileDataRequest request, + IEnumerable> originalContent, + IEnumerable> newContent, + List headers, + bool recordIsWrappedInQuotes) + { + var delimiter = request.DelimiterType.GetNameFromValue(); + var wrapText = recordIsWrappedInQuotes ? "\"" : string.Empty; + var destinationFileExists = File.Exists(request.FinalDestinationIndexFilePath); + + if (request.IndexHasHeaders) + { + originalContent = originalContent.Prepend(headers); + + if (!request.AppendToDestinationIndexFile || !destinationFileExists) + { + newContent = !request.ExtractAllIndexes && request.IndexesToExtract.Count > 0 + ? newContent.Prepend(headers.Where((_, i) => request.IndexesToExtract.Contains(i)).ToList()) + : newContent.Prepend(headers); + } + } + + var originalFile = TransformToOutputString(originalContent, delimiter, wrapText); + var newFile = TransformToOutputString(newContent, delimiter, wrapText); + + if (request.AppendToDestinationIndexFile && destinationFileExists) + { + File.AppendAllText(request.FinalDestinationIndexFilePath, Environment.NewLine + newFile); + } + else + { + File.WriteAllText(request.FinalDestinationIndexFilePath, newFile); + } + + File.WriteAllText(request.FinalIndexFilePath, originalFile); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/FileActionService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/FileActionService.cs new file mode 100644 index 0000000..a3bba8a --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/FileActionService.cs @@ -0,0 +1,9 @@ +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Library.Internal.LCUServiceFactory.Services; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public abstract class FileActionService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : LcuAbstractService(servicesFactoryConfiguration.GetServiceFactoryRegistrationByInterface()) +{ +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/FileSizeService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/FileSizeService.cs new file mode 100644 index 0000000..3e1d1b9 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/FileSizeService.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.IO; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.FileOperationsModels.Enums; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Models.Requests; +using LCU.Library.Internal.FileOperationsModels.Models.Responses; +using LCU.Library.Internal.FileOperationsModels.Shared; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class FileSizeService : IFileSizeService +{ + + public List GetFileSize(FileSizeRequest fileSizeRequest) + { + var fileSizeResponses = new List(); + try + { + var requests = DynamicNameBuilder.BuildListOfRequests(fileSizeRequest); + foreach (var request in requests) + { + var response = DetermineFileSize(request); + fileSizeResponses.Add(response); + } + + return fileSizeResponses; + } + catch (Exception exception) + { + throw new Exception($"Failed to get file size. Exception: {exception.Message}"); + } + } + + public decimal ConvertFileSize(decimal fileSizeInBytes, FileSizeTypes returnFileSizeType) + => returnFileSizeType switch + { + FileSizeTypes.Byte => fileSizeInBytes, + FileSizeTypes.Kilobyte => fileSizeInBytes/1024, + FileSizeTypes.Megabyte => fileSizeInBytes/1024/1024, + FileSizeTypes.Gigabyte => fileSizeInBytes/1024/1024/1024, + FileSizeTypes.Terabyte => fileSizeInBytes/1024/1024/1024/1024, + _ => throw new ArgumentOutOfRangeException(nameof(returnFileSizeType), $"Not expected FileSizeType value: {returnFileSizeType}"), + }; + + public RequestResponse PreQueueValidation(FileSizeRequest fileSizeRequest) + { + var requestResponse = new RequestResponse(); + try + { + var requestList = DynamicNameBuilder.BuildListOfRequests(fileSizeRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requestList, fileSizeRequest.FileSourceRequest.SourcePath, requestResponse); + foreach (var request in requestList) + { + requestResponse = IsRequestValid(request, requestResponse); + } + + return requestResponse; + } + catch (Exception exception) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add(exception.Message); + return requestResponse; + } + } + + private static RequestResponse IsRequestValid(FileSizeRequest request, RequestResponse requestResponse) + { + return FileValidation.ValidateFileExists(request.FileSourceRequest.Source, requestResponse); + } + + private FileSizeResponse DetermineFileSize(FileSizeRequest request) + { + var file = new FileInfo(request.FileSourceRequest.Source); + var length = Convert.ToDecimal(file.Length); + return new FileSizeResponse + { + FileName = file.Name, + FileSize = ConvertFileSize(length, request.FileSizeType), + FileSizeType = request.FileSizeType + }; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IActionService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IActionService.cs new file mode 100644 index 0000000..1b66d39 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IActionService.cs @@ -0,0 +1,9 @@ +using LCU.Library.Internal.FileOperationsModels.Models; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public interface IActionService +{ + RequestResponse PerformRequest(TRequest request); + RequestResponse PreQueueValidation(TRequest originalRequest); + RequestResponse IsRequestValid(TRequest request, RequestResponse requestResponse); +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IFileActionService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IFileActionService.cs new file mode 100644 index 0000000..a0b43fb --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IFileActionService.cs @@ -0,0 +1,10 @@ +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Services; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices +{ + public interface IFileActionService : ILcuService + { + RequestResponse ExecuteAction(QueueRequest queueRequest); + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IFileSizeService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IFileSizeService.cs new file mode 100644 index 0000000..31a8400 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/IFileSizeService.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using LCU.Library.Internal.FileOperationsModels.Enums; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Models.Requests; +using LCU.Library.Internal.FileOperationsModels.Models.Responses; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public interface IFileSizeService +{ + public List GetFileSize(FileSizeRequest request); + public decimal ConvertFileSize(decimal fileSizeInBytes, FileSizeTypes returnFileSizeType); + public RequestResponse PreQueueValidation(FileSizeRequest originalRequest); +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ModifyFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ModifyFileService.cs new file mode 100644 index 0000000..7b97cf7 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ModifyFileService.cs @@ -0,0 +1,99 @@ +using Newtonsoft.Json; +using System; +using System.IO; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Library.Internal.LCUServiceFactory.Configurations; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class ModifyFileService : FileActionService, IFileActionService, IActionService +{ + public ModifyFileService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var fileRequest = JsonConvert.DeserializeObject(queueRequest.Request); + var requests = DynamicNameBuilder.BuildListOfRequest(fileRequest); + + requestResponse = FileValidation.ValidateFileRequestsWithinLimit(requests, fileRequest.MaxAmountOfFiles, requestResponse); + + foreach (var request in requests) + { + var temporaryRequestResponse = PerformRequest(request); + if (!temporaryRequestResponse.IsSuccessful) + { + requestResponse.IsSuccessful = fileRequest.SucceedOnError; + if (fileRequest.SucceedOnError) + { + requestResponse.MessageStack.Add("Succeed On Error is set."); + } + + requestResponse.MessageStack.AddRange(temporaryRequestResponse.MessageStack); + } + } + + requestResponse.ReturnResponse = fileRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(ModifyFileRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, ModifyFile); + } + + private void ModifyFile(ModifyFileRequest request) + { + try + { + File.Move(request.OriginalSource, request.FinalSource); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(ModifyFileRequest originalRequest) + { + var requestResponse = new RequestResponse(); + var requestList = DynamicNameBuilder.BuildListOfRequest(originalRequest); + requestResponse = FileValidation.ValidateFileRequestsWithinLimit(requestList, originalRequest.MaxAmountOfFiles, requestResponse); + foreach (var request in requestList) + { + requestResponse = IsRequestValid(request, requestResponse); + } + + return requestResponse; + } + + public RequestResponse IsRequestValid(ModifyFileRequest fileRequest, RequestResponse requestResponse) + { + if (fileRequest.SucceedOnError) + { + return requestResponse; + } + + requestResponse = FileValidation.ValidateDirectoryExists(fileRequest.SourcePath, requestResponse); + requestResponse = FileValidation.ValidateFileExists(fileRequest.OriginalSource, requestResponse); + requestResponse = FileValidation.ValidateFileNotExists(fileRequest.FinalSource, requestResponse); + + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/MoveFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/MoveFileService.cs new file mode 100644 index 0000000..75f06b5 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/MoveFileService.cs @@ -0,0 +1,105 @@ +using System; +using System.IO; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Api.Internal.FileOperations.Validations; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class MoveFileService : FileActionService, IFileActionService, IActionService +{ + public MoveFileService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var fileRequest = JsonConvert.DeserializeObject(queueRequest.Request); + var requests = DynamicNameBuilder.BuildListOfRequest(fileRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requests, fileRequest.Source, fileRequest.SucceedOnError, requestResponse); + + foreach (var request in requests) + { + var temporaryRequestResponse = PerformRequest(request); + if (!temporaryRequestResponse.IsSuccessful) + { + requestResponse.IsSuccessful = fileRequest.SucceedOnError; + if (fileRequest.SucceedOnError) + { + requestResponse.MessageStack.Add("Succeed On Error is set."); + } + + requestResponse.MessageStack.AddRange(temporaryRequestResponse.MessageStack); + } + } + + requestResponse.ReturnResponse = fileRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(MoveFileRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, MoveFile); + } + + private void MoveFile(MoveFileRequest request) + { + try + { + File.Move(request.Source, request.Destination, request.Overwrite); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(MoveFileRequest originalRequest) + { + var requestResponse = new RequestResponse(); + var requestList = DynamicNameBuilder.BuildListOfRequest(originalRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requestList, originalRequest.SourcePath, originalRequest.SucceedOnError, requestResponse); + foreach (var request in requestList) + { + requestResponse = IsRequestValid(request, requestResponse); + } + + return requestResponse; + } + + public RequestResponse IsRequestValid(MoveFileRequest fileRequest, RequestResponse requestResponse) + { + if (fileRequest.SucceedOnError) + { + return requestResponse; + } + + requestResponse = FileValidation.ValidateSourceNotEmpty(fileRequest.Source, requestResponse); + requestResponse = FileValidation.ValidateFileExists(fileRequest.Source, requestResponse); + + requestResponse = FileValidation.ValidateDestinationNotEmpty(fileRequest.Destination, requestResponse); + requestResponse = FileValidation.ValidateDirectoryExists(fileRequest.DestinationPath, requestResponse); + + if (!fileRequest.Overwrite) + { + requestResponse = FileValidation.ValidateFileNotExists(fileRequest.Destination, requestResponse); + } + + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/OperationService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/OperationService.cs new file mode 100644 index 0000000..7986ca8 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/OperationService.cs @@ -0,0 +1,30 @@ +using System; +using LCU.Library.Internal.FileOperationsModels.Models; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public static class OperationService +{ + public static RequestResponse PerformOperation(T request, + Func validate, + Action action) + { + var requestResponse = new RequestResponse(); + try + { + + requestResponse = validate(request, requestResponse); + if (requestResponse.IsSuccessful) + { + action(request); + } + + return requestResponse; + } + catch (Exception exception) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add(exception.Message); + return requestResponse; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/RenameDirectoryService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/RenameDirectoryService.cs new file mode 100644 index 0000000..44dfca8 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/RenameDirectoryService.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Api.Internal.FileOperations.Validations; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class RenameDirectoryService : FileActionService, IFileActionService, IActionService +{ + public RenameDirectoryService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var directoryRequest = JsonConvert.DeserializeObject(queueRequest.Request); + requestResponse = PerformRequest(directoryRequest); + requestResponse.ReturnResponse = directoryRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(RenameDirectoryRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, RenameDirectory); + } + + private void RenameDirectory(RenameDirectoryRequest request) + { + try + { + Directory.Move(request.FinalSourceDirectoryPath, request.NewDirectoryDestination); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(RenameDirectoryRequest originalRequest) + { + var requestResponse = new RequestResponse(); + requestResponse = IsRequestValid(originalRequest, requestResponse); + return requestResponse; + } + + public RequestResponse IsRequestValid(RenameDirectoryRequest request, RequestResponse requestResponse) + { + requestResponse = FileValidation.ValidateDirectoryExists(request.SourceDirectoryPath, requestResponse); + requestResponse = FileValidation.ValidateDirectoryExists(request.FinalSourceDirectoryPath, requestResponse); + requestResponse = FileValidation.ValidateDirectoryNotExists(request.NewDirectoryDestination, requestResponse); + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/RenameFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/RenameFileService.cs new file mode 100644 index 0000000..7740a4d --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/RenameFileService.cs @@ -0,0 +1,85 @@ +using System; +using System.IO; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Api.Internal.FileOperations.Validations; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class RenameFileService : FileActionService, IFileActionService, IActionService +{ + public RenameFileService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var fileRequest = JsonConvert.DeserializeObject(queueRequest.Request); + requestResponse = PerformRequest(fileRequest); + if (!requestResponse.IsSuccessful) + { + requestResponse.IsSuccessful = fileRequest.SucceedOnError; + if (fileRequest.SucceedOnError) + { + requestResponse.MessageStack.Add("Succeed On Error is set."); + } + } + + requestResponse.ReturnResponse = fileRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(RenameFileRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, RenameFile); + } + + private void RenameFile(RenameFileRequest request) + { + try + { + File.Move(request.Source, request.NewFilenamePath); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(RenameFileRequest originalRequest) + { + var requestResponse = new RequestResponse(); + requestResponse = IsRequestValid(originalRequest, requestResponse); + return requestResponse; + } + + public RequestResponse IsRequestValid(RenameFileRequest fileRequest, RequestResponse requestResponse) + { + if (fileRequest.SucceedOnError) + { + return requestResponse; + } + + requestResponse = FileValidation.ValidateSourceNotEmpty(fileRequest.Source, requestResponse); + requestResponse = FileValidation.ValidateFileExists(fileRequest.Source, requestResponse); + + requestResponse = FileValidation.ValidateFilenameNotEmpty(fileRequest.FinalNewFileName, requestResponse); + requestResponse = FileValidation.ValidateFilename(fileRequest.FinalNewFileName, requestResponse); + requestResponse = FileValidation.ValidateFileNotExists(fileRequest.NewFilenamePath, requestResponse); + + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/UnzipFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/UnzipFileService.cs new file mode 100644 index 0000000..89b079f --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/UnzipFileService.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.IO.Compression; +using System.IO; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class UnzipFileService : FileActionService, IFileActionService, IActionService +{ + public UnzipFileService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var unzipRequest = JsonConvert.DeserializeObject(queueRequest.Request); + var requests = BuildFileList(unzipRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requests, unzipRequest.FormattedSourceDirectoryPath, requestResponse); + + foreach (var request in requests) + { + var temporaryRequestResponse = PerformRequest(request); + if (!temporaryRequestResponse.IsSuccessful) + { + requestResponse.IsSuccessful = temporaryRequestResponse.IsSuccessful; + requestResponse.MessageStack.AddRange(temporaryRequestResponse.MessageStack); + } + } + + requestResponse.ReturnResponse = unzipRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(UnzipFileRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, UnzipFile); + } + + private void UnzipFile(UnzipFileRequest request) + { + try + { + var destination = request.FormattedDestinationPath; + if (request.UseZipFileNameAsFinalDestination) + { + destination = Path.GetDirectoryName(request.DestinationPathZipFileNameWithoutZip); + if (destination.Length > 0) + { + Directory.CreateDirectory(destination); + } + } + + ZipFile.ExtractToDirectory(request.SourceFilePath, destination); + + if (request.IgnoreFileNameSubFolderAsDestination) + { + MoveFromChildFolder(destination, request); + } + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + private void MoveFromChildFolder(string destination, UnzipFileRequest request) + { + var recursionDepth = request.MaxFileDepth; + var currentDepth = 0; + var currentFilePath = destination; + + RecursiveFolderSearch(destination, recursionDepth, currentFilePath,currentDepth); + } + + public void RecursiveFolderSearch(string destination, int recursionDepth, string currentFilePath, int currentDepth) + { + if (currentDepth>recursionDepth) + { + return; + } + + var folders = Directory.EnumerateDirectories(currentFilePath); + foreach (var folder in folders) + { + RecursiveFolderSearch(destination, recursionDepth, folder, currentDepth + 1); + var childFiles = Directory.EnumerateFiles(folder); + + foreach (var file in childFiles) + { + var fileName = file[(file.LastIndexOf("\\") + 1)..]; + File.Move(file, destination + "\\" + fileName); + } + Directory.Delete(folder); + } + } + + public RequestResponse PreQueueValidation(UnzipFileRequest originalRequest) + { + var requestResponse = new RequestResponse(); + var requestList = BuildFileList(originalRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requestList, originalRequest.FormattedSourceDirectoryPath, requestResponse); + foreach (var request in requestList) + { + requestResponse = IsRequestValid(request, requestResponse); + } + + return requestResponse; + } + + public List BuildFileList(UnzipFileRequest originalRequest) + { + var nameList = RegexNameHandler.BuildFileList(originalRequest.FormattedSourceDirectoryPath, + originalRequest.FinalZipFileName, + originalRequest.FileNameSetting); + var requests = new List(); + + foreach (var name in nameList) + { + var newRequest = Clone.DeepClone(originalRequest); + newRequest.ZipFileName = name; + + requests.Add(newRequest); + } + + return requests; + } + + public RequestResponse IsRequestValid(UnzipFileRequest fileRequest, RequestResponse requestResponse) + { + requestResponse = DirectoryValidation.ValidateDirectoryExists(fileRequest.FormattedSourceDirectoryPath, requestResponse); + requestResponse = FileValidation.ValidateFileExists(fileRequest.SourceFilePath, requestResponse); + requestResponse = DirectoryValidation.ValidateDirectoryExists(fileRequest.FormattedDestinationPath, requestResponse); + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/UnzipPasswordProtectedFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/UnzipPasswordProtectedFileService.cs new file mode 100644 index 0000000..bf9c800 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/UnzipPasswordProtectedFileService.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using ICSharpCode.SharpZipLib.Zip; +using ICSharpCode.SharpZipLib.Core; +using System.IO; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.LCUServiceFactory.Configurations; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class UnzipPasswordProtectedFileService : FileActionService, IFileActionService, IActionService +{ + public UnzipPasswordProtectedFileService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var unzipPasswordProtectedFileRequest = JsonConvert.DeserializeObject(queueRequest.Request); + var requests = BuildFileList(unzipPasswordProtectedFileRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requests, unzipPasswordProtectedFileRequest.FormattedSourceDirectoryPath, requestResponse); + + foreach (var request in requests) + { + var temporaryRequestResponse = PerformRequest(request); + if (!temporaryRequestResponse.IsSuccessful) + { + requestResponse.IsSuccessful = temporaryRequestResponse.IsSuccessful; + requestResponse.MessageStack.AddRange(temporaryRequestResponse.MessageStack); + } + } + + requestResponse.ReturnResponse = unzipPasswordProtectedFileRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(UnzipPasswordProtectedFileRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, UnzipPasswordProtectedFile); + } + + public void UnzipPasswordProtectedFile(UnzipPasswordProtectedFileRequest request) + { + ZipFile passwordProtectedZipFile = null; + try + { + var fileStream = File.OpenRead(request.SourceFilePath); + passwordProtectedZipFile = new ZipFile(fileStream) + { + Password = request.Password + }; + + foreach (ZipEntry zipEntry in passwordProtectedZipFile) + { + if (!zipEntry.IsFile) + { + // Ignore directories + continue; + } + + var entryFileName = zipEntry.Name; + // to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName); + // Optionally match entrynames against a selection list here to skip as desired. + // The unpacked length is available in the zipEntry.Size property. + + // 4K is optimum + var buffer = new byte[4096]; + var zipStream = passwordProtectedZipFile.GetInputStream(zipEntry); + + // Manipulate the output filename here as desired. + var finalDestinationPath = request.FormattedDestinationPath; + if (request.UseZipFileNameAsFinalDestination) + { + var zipFilename = request.PasswordProtectedZipFileNameWithDate.ToUpper().EndsWith(".ZIP") ? + request.PasswordProtectedZipFileNameWithDate.ToUpper().Replace(".ZIP", "") : + request.PasswordProtectedZipFileNameWithDate; + finalDestinationPath = $"{finalDestinationPath}{zipFilename}\\"; + } + + var fullZipToPath = Path.Combine(finalDestinationPath, entryFileName); + var directoryName = Path.GetDirectoryName(fullZipToPath); + if (directoryName.Length > 0) + { + Directory.CreateDirectory(directoryName); + } + + // Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size + // of the file, but does not waste memory. + // The "using" will close the stream even if an exception occurs. + using (FileStream streamWriter = File.Create(fullZipToPath)) + { + StreamUtils.Copy(zipStream, streamWriter, buffer); + } + } + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + finally + { + if (passwordProtectedZipFile != null) + { + passwordProtectedZipFile.IsStreamOwner = true; // Makes close also shut the underlying stream + passwordProtectedZipFile.Close(); // Ensure we release resources + } + } + + } + + public RequestResponse PreQueueValidation(UnzipPasswordProtectedFileRequest originalRequest) + { + var requestResponse = new RequestResponse(); + var requestList = BuildFileList(originalRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requestList, originalRequest.FormattedSourceDirectoryPath, requestResponse); + foreach (var request in requestList) + { + requestResponse = IsRequestValid(request, requestResponse); + } + + return requestResponse; + } + + public List BuildFileList(UnzipPasswordProtectedFileRequest originalRequest) + { + var nameList = RegexNameHandler.BuildFileList(originalRequest.FormattedSourceDirectoryPath, + originalRequest.FinalPasswordProtectedZipFileName, + originalRequest.FileNameSetting); + var requests = new List(); + + foreach (var name in nameList) + { + var newRequest = Clone.DeepClone(originalRequest); + newRequest.PasswordProtectedZipFileName = name; + + requests.Add(newRequest); + } + + return requests; + } + + public RequestResponse IsRequestValid(UnzipPasswordProtectedFileRequest fileRequest, RequestResponse requestResponse) + { + requestResponse = DirectoryValidation.ValidateDirectoryExists(fileRequest.FormattedSourceDirectoryPath, requestResponse); + requestResponse = FileValidation.ValidateFileExists(fileRequest.SourceFilePath, requestResponse); + requestResponse = DirectoryValidation.ValidateDirectoryExists(fileRequest.FormattedDestinationPath, requestResponse); + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ValidateFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ValidateFileService.cs new file mode 100644 index 0000000..6088d78 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ValidateFileService.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Library.Internal.FileOperationsModels.Enums; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.FileOperationsModels.Models.Requests; +using Microsoft.OpenApi.Extensions; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class ValidateFileService : FileActionService, IFileActionService, IActionService +{ + public ValidateFileService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var fileRequest = JsonConvert.DeserializeObject(queueRequest.Request); + var requests = DynamicNameBuilder.BuildListOfRequests(fileRequest); + + if (fileRequest.FileDoesExist) + { + requestResponse = FileValidation.ValidateIfAnyRequest(requests, fileRequest.FileSourceRequest.Source, requestResponse); + } + + if (fileRequest.CheckFileCount) + { + if (!ValidateFileCount(fileRequest, requests)) + { + requestResponse.MessageStack.Add($"The file count condition returned false: {requests.Count} {fileRequest.OperatorType.GetDisplayName()} {fileRequest.FileCount} "); + requestResponse.IsSuccessful = false; + } + } + + requestResponse = PerformRequests(requests, requestResponse); + + requestResponse.ReturnResponse = fileRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public bool ValidateFileCount(ValidateFileRequest fileRequest, List requests) + => fileRequest.OperatorType switch + { + OperatorTypes.Equals => (requests.Count == fileRequest.FileCount), + OperatorTypes.GreaterThan => (requests.Count > fileRequest.FileCount), + OperatorTypes.GreaterThanEquals => (requests.Count >= fileRequest.FileCount ), + OperatorTypes.LessThan => (requests.Count < fileRequest.FileCount), + OperatorTypes.LessThanEquals => (requests.Count <= fileRequest.FileCount ), + _ => throw new ArgumentOutOfRangeException(nameof(fileRequest.OperatorType), $"Not expected OperatorType value: {fileRequest.OperatorType}"), + }; + + + public RequestResponse PerformRequests(List requests, RequestResponse requestResponse) + { + foreach (var request in requests) + { + var temporaryRequestResponse = PerformRequest(request); + if (!temporaryRequestResponse.IsSuccessful) + { + requestResponse.MessageStack.AddRange(temporaryRequestResponse.MessageStack); + requestResponse.IsSuccessful = false; + } + } + return requestResponse; + } + + public RequestResponse PerformRequest(ValidateFileRequest request) + { + var requestResponse = new RequestResponse(); + if (request.FileDoesExist) + { + return FileValidation.ValidateFileExists(request.FileSourceRequest.Source, requestResponse); + } + return FileValidation.ValidateFileNotExists(request.FileSourceRequest.Source, requestResponse); + } + + public RequestResponse PreQueueValidation(ValidateFileRequest originalRequest) + { + var requestResponse = new RequestResponse(); + requestResponse = IsRequestValid(originalRequest, requestResponse); + return requestResponse; + } + + public RequestResponse IsRequestValid(ValidateFileRequest request, RequestResponse requestResponse) + { + requestResponse = FileValidation.ValidateSourceNotEmpty(request.FileSourceRequest.SourceFile, requestResponse); + + requestResponse = FileValidation.ValidateFilenameNotEmpty(request.FileSourceRequest.SourceFile, requestResponse); + requestResponse = FileValidation.ValidateFilename(request.FileSourceRequest.SourceFile, requestResponse); + + return requestResponse; + } +} + diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ValidateFileSizeInRangeService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ValidateFileSizeInRangeService.cs new file mode 100644 index 0000000..c142adf --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ValidateFileSizeInRangeService.cs @@ -0,0 +1,111 @@ +using System; +using System.IO; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.FileOperationsModels.Enums; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Models.Requests; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using Newtonsoft.Json; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class ValidateFileSizeInRangeService : FileActionService, IFileActionService, IActionService +{ + private readonly IFileSizeService _fileSizeService; + + public ValidateFileSizeInRangeService(IFileSizeService fileSizeService, + ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + _fileSizeService = fileSizeService; + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var fileRequest = JsonConvert.DeserializeObject(queueRequest.Request); + var requests = DynamicNameBuilder.BuildListOfRequests(fileRequest); + + requestResponse = FileValidation.ValidateIfAnyRequest(requests, fileRequest.FileSourceRequest.Source, requestResponse); + + foreach (var request in requests) + { + var temporaryRequestResponse = PerformRequest(request); + if (temporaryRequestResponse.IsSuccessful) + { + continue; + } + + requestResponse.MessageStack.AddRange(temporaryRequestResponse.MessageStack); + requestResponse.IsSuccessful = false; + } + + requestResponse.ReturnResponse = fileRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(ValidateFileSizeInRangeRequest request) + { + var requestResponse = new RequestResponse(); + return ValidateFileSizeInRange(request.FileSourceRequest.Source, + request.StartFileSizeRange, + request.EndFileSizeRange, + request.FileSizeType, + requestResponse); + } + + public RequestResponse PreQueueValidation(ValidateFileSizeInRangeRequest originalRequest) + { + var requestResponse = new RequestResponse(); + try + { + var requestList = DynamicNameBuilder.BuildListOfRequests(originalRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requestList, originalRequest.FileSourceRequest.SourcePath, requestResponse); + foreach (var request in requestList) + { + requestResponse = IsRequestValid(request, requestResponse); + } + + return requestResponse; + } + catch (Exception exception) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add(exception.Message); + return requestResponse; + } + } + + public RequestResponse IsRequestValid(ValidateFileSizeInRangeRequest request, RequestResponse requestResponse) + { + requestResponse = FileValidation.ValidateFileExists(request.FileSourceRequest.Source, requestResponse); + + return requestResponse; + } + + private RequestResponse ValidateFileSizeInRange(string path, decimal min, decimal max, + FileSizeTypes fileSizeType, + RequestResponse requestResponse) + { + decimal fileLengthInBytes = new FileInfo(path).Length; + var fileLength = _fileSizeService.ConvertFileSize(fileLengthInBytes, fileSizeType); + + if (fileLength < min || fileLength > max) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add($"File size is not in range at path {path}"); + } + + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/WriteFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/WriteFileService.cs new file mode 100644 index 0000000..449f65d --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/WriteFileService.cs @@ -0,0 +1,78 @@ +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +using Newtonsoft.Json; +using System; +using System.IO; + +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices +{ + public class WriteFileService : FileActionService, IFileActionService, IActionService + { + public WriteFileService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + + try + { + var fileRequest = JsonConvert.DeserializeObject(queueRequest.Request); + + requestResponse = PerformRequest(fileRequest); + + requestResponse.ReturnResponse = fileRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(WriteFileRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, WriteFile); + } + + private void WriteFile(WriteFileRequest request) + { + try + { + File.WriteAllText(request.FullFilePath, request.FileData); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(WriteFileRequest originalRequest) + { + var requestResponse = new RequestResponse(); + + requestResponse = IsRequestValid(originalRequest, requestResponse); + + return requestResponse; + } + + public RequestResponse IsRequestValid(WriteFileRequest originalRequest, RequestResponse requestResponse) + { + requestResponse = FileValidation.ValidateDirectoryExists(originalRequest.DestinationPath, requestResponse); + + if (!originalRequest.Overwrite) + { + requestResponse = FileValidation.ValidateFileNotExists(originalRequest.FullFilePath, requestResponse); + } + + return requestResponse; + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ZipDirectoryService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ZipDirectoryService.cs new file mode 100644 index 0000000..da4d2f3 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileActionServices/ZipDirectoryService.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.IO.Compression; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +namespace LCU.Api.Internal.FileOperations.Services.FileActionServices; + +public class ZipDirectoryService : FileActionService, IFileActionService, IActionService +{ + public ZipDirectoryService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var zipRequest = JsonConvert.DeserializeObject(queueRequest.Request); + var requests = BuildDirectoryList(zipRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requests, zipRequest.SourceDirectory, requestResponse); + var result = requestResponse.IsSuccessful; + + foreach (var request in requests) + { + var temporaryRequestResponse = PerformRequest(request); + if (!temporaryRequestResponse.IsSuccessful) + { + requestResponse.IsSuccessful = temporaryRequestResponse.IsSuccessful; + requestResponse.MessageStack.AddRange(temporaryRequestResponse.MessageStack); + } + } + + requestResponse.ReturnResponse = zipRequest; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(ZipDirectoryRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, ZipDirectory); + } + + private void ZipDirectory(ZipDirectoryRequest request) + { + try + { + ZipFile.CreateFromDirectory(request.SourceDirectory, request.ZipDestination); + } + catch (Exception exception) + { + throw new Exception($"Exception thrown while trying to ExecuteRequestAction. " + + $"Request: {JsonConvert.SerializeObject(request)}" + + $"ExceptionMessage: {exception.Message}"); + } + } + + public RequestResponse PreQueueValidation(ZipDirectoryRequest originalRequest) + { + var requestResponse = new RequestResponse(); + var requestList = BuildDirectoryList(originalRequest); + requestResponse = FileValidation.ValidateIfAnyRequest(requestList, originalRequest.FormattedSourceDirectory, requestResponse); + foreach (var request in requestList) + { + requestResponse = IsRequestValid(request, requestResponse); + } + + return requestResponse; + } + + public List BuildDirectoryList(ZipDirectoryRequest originalRequest) + { + var nameList = RegexNameHandler.BuildDirectoryList(originalRequest.SourceDirectory, + originalRequest.DynamicDirectoryName, + originalRequest.FileNameSetting); + + var requests = new List(); + + foreach (var name in nameList) + { + var newRequest = Clone.DeepClone(originalRequest); + newRequest.DirectoryName = name; + newRequest.SourceDirectory = String.IsNullOrWhiteSpace(newRequest.DirectoryName) ? + originalRequest.SourceDirectory : + $"{originalRequest.FormattedSourceDirectory}{newRequest.DirectoryName}"; + + requests.Add(newRequest); + } + + return requests; + } + + public RequestResponse IsRequestValid(ZipDirectoryRequest fileRequest, RequestResponse requestResponse) + { + requestResponse = DirectoryValidation.ValidateDirectoryExists(fileRequest.SourceDirectory, requestResponse); + requestResponse = DirectoryValidation.ValidateDirectoryExists(fileRequest.ZipPath, requestResponse); + requestResponse = FileValidation.ValidateFileNotExists(fileRequest.ZipDestination, requestResponse); + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileOperationsService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileOperationsService.cs new file mode 100644 index 0000000..f0d1fbd --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/FileOperationsService.cs @@ -0,0 +1,87 @@ +using System; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Serilog; +using LCU.Library.Internal.ApiResponse.Services; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.LCUServiceFactory.Services; +using LCU.Api.Internal.FileOperations.Services.QueueServices; +using LCU.Api.Internal.FileOperations.Services.FileActionServices; +using LCU.Api.Internal.FileOperations.Configurations; +namespace LCU.Api.Internal.FileOperations.Services; + +public class FileOperationsService : IFileOperationsService +{ + private readonly IQueueFileOperationsService _queueFileOperationsService; + private readonly ApiConfiguration _apiConfiguration; + + private readonly ILcuServiceFactory _lcuServiceFactory; + + public FileOperationsService(IQueueFileOperationsService queueFileOperationsService, + ApiConfiguration apiConfiguration, + ILcuServiceFactory lcuServiceFactory) + { + _queueFileOperationsService = queueFileOperationsService; + _apiConfiguration = apiConfiguration; + _lcuServiceFactory = lcuServiceFactory; + } + + public async Task HandleFileOperationsRequest(QueueRequest queueRequest) + { + var result = PerformFileOperation(queueRequest); + + if (ShouldPerformFileOperationsAgain(queueRequest, result)) + { + Log.Information($"Attempting to requeue file operations request. Request: {JsonConvert.SerializeObject(queueRequest)}"); + queueRequest.Attempts++; + await _queueFileOperationsService.QueueFileOperationsRequest(queueRequest); + return; + } + + await QueueReturnResponse.QueueResponse(queueRequest.ReturnResponse, result.IsSuccessful, result.MessageStack); + + if (!result.IsSuccessful) + { + var errorMessage = String.Join("; ", result.MessageStack.ToArray()); + throw new ServiceException($"File Operation Request was not successful. MessageStack:{errorMessage}"); + } + } + + public RequestResponse PerformFileOperation(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + ArgumentNullException.ThrowIfNull(queueRequest); + + var fileActionService = _lcuServiceFactory.GetService(queueRequest.FileOperationType); + + return fileActionService.ExecuteAction(queueRequest); + } + catch (Exception exception) + { + var failureMessage = $"Failed to perform file operations."; + Log.Error(exception, failureMessage); + + var fileOperationType = queueRequest?.FileOperationType; + if (fileOperationType == null) + { + requestResponse.MessageStack.Add($"{failureMessage}. FileOperationType is Null. " + + $"ExceptionMessage: {exception.Message}"); + return requestResponse; + } + + requestResponse = RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, queueRequest.FileOperationType, exception.Message); + + return requestResponse; + } + } + + public bool ShouldPerformFileOperationsAgain(QueueRequest request, RequestResponse requestResponse) + { + return !requestResponse.IsSuccessful && + request.Attempts != null && + request.Attempts < _apiConfiguration.AttemptsLimit; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDirectFileOperationsService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDirectFileOperationsService.cs new file mode 100644 index 0000000..30aa1cb --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDirectFileOperationsService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.ApiResponse.Models; + +namespace LCU.Api.Internal.FileOperations.Services; + +public interface IDirectFileOperationsService +{ + Task HandleDirectFileOperationsRequest(TRequest request, + FileOperationType fileOperationType) where TRequest : ReturnResponse; +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDirectFileService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDirectFileService.cs new file mode 100644 index 0000000..b2c63d8 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDirectFileService.cs @@ -0,0 +1,7 @@ +namespace LCU.Api.Internal.FileOperations.Services +{ + public interface IDirectFileService + { + byte[] GetFileBytes(string path); + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDynamicDirectoryService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDynamicDirectoryService.cs new file mode 100644 index 0000000..a9d856a --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IDynamicDirectoryService.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; +using LCU.Library.Internal.FileOperationsModels.Models; +namespace LCU.Api.Internal.FileOperations.Services; + +public interface IDynamicDirectoryService +{ + public IEnumerable BuildDirectoryList(DynamicDirectoryListRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IFileOperationsService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IFileOperationsService.cs new file mode 100644 index 0000000..f45fd6e --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/IFileOperationsService.cs @@ -0,0 +1,10 @@ +using LCU.Library.Internal.FileOperationsModels.Models; +using System.Threading.Tasks; + +namespace LCU.Api.Internal.FileOperations.Services; + +public interface IFileOperationsService +{ + RequestResponse PerformFileOperation(QueueRequest queueRequest); + Task HandleFileOperationsRequest(QueueRequest queueRequest); +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/BatchPdfParserService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/BatchPdfParserService.cs new file mode 100644 index 0000000..5518c8f --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/BatchPdfParserService.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; +using System.Text; +using PdfSharp.Pdf; +using PdfSharp.Pdf.IO; +using Serilog; +using Newtonsoft.Json; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Api.Internal.FileOperations.Services.FileActionServices; +using LCU.Api.Internal.FileOperations.Validations; +using LCU.Library.Internal.LCUServiceFactory.Configurations; +namespace LCU.Api.Internal.FileOperations.Services.PdfServices; + +public class BatchPdfParserService : FileActionService, IFileActionService, IBatchPdfParserService +{ + public BatchPdfParserService(ServicesFactoryConfiguration servicesFactoryConfiguration) + : base(servicesFactoryConfiguration) + { + } + + public RequestResponse ExecuteAction(QueueRequest queueRequest) + { + var requestResponse = new RequestResponse(); + try + { + var request = JsonConvert.DeserializeObject(queueRequest.Request); + requestResponse = PerformRequest(request); + requestResponse.ReturnResponse = request; + return requestResponse; + } + catch (Exception exception) + { + return RequestResponseHelper.ExceptionUpdateRequestResponse(requestResponse, + queueRequest.FileOperationType, + exception.Message); + } + } + + public RequestResponse PerformRequest(BatchParsePdfRequest request) + { + return OperationService.PerformOperation(request, IsRequestValid, BatchParsePdfs); + } + + public void BatchParsePdfs(BatchParsePdfRequest batchParsePdfRequest) + { + try + { + var inputDocument = PdfReader.Open(batchParsePdfRequest.FullPdfPath, PdfDocumentOpenMode.Import); + foreach(var batchPdf in batchParsePdfRequest.BatchPdfs) + { + ParseAndSavePdf(batchParsePdfRequest, batchPdf, inputDocument); + } + } + catch (Exception exception) + { + Log.Error(exception, $"Failure to Batch Parse Pdfs. ParsePdfRequest:{JsonConvert.SerializeObject(batchParsePdfRequest)}"); + throw new Exception($"Failure to Batch Parse Pdfs. ExceptionMessage:{exception.Message}"); + } + } + + public void ParseAndSavePdf(BatchParsePdfRequest batchParsePdfRequest, BatchPdf batchPdf, PdfDocument inputDocument) + { + var outputDocument = AddPdfPagesToOutputDocument(batchPdf.PageStart, batchPdf.PageStop, inputDocument); + string name = Path.GetFileNameWithoutExtension(batchParsePdfRequest.PdfName); + var newPdfName = string.IsNullOrWhiteSpace(batchPdf.ParsedPdfname) ? $"{name}.{batchPdf.PageStart}.pdf" : batchPdf.ParsedPdfname; + var newPath = Path.Combine(batchParsePdfRequest.RootPath, newPdfName); + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + outputDocument.Save(newPath); + } + + public PdfDocument AddPdfPagesToOutputDocument(int pageStart, int pageStop, PdfDocument inputDocument) + { + var startIndex = pageStart - 1; + var stopIndex = pageStop - 1; + var outputDocument = new PdfDocument(); + for (int page = startIndex; page <= stopIndex; page++) + { + outputDocument.Version = inputDocument.Version; + outputDocument.Info.Title = inputDocument.Info.Title; + outputDocument.Info.Creator = inputDocument.Info.Creator; + outputDocument.AddPage(inputDocument.Pages[page]); + } + + return outputDocument; + } + + public RequestResponse IsRequestValid(BatchParsePdfRequest request, RequestResponse requestResponse) + { + requestResponse = DirectoryValidation.ValidateDirectoryExists(request.RootPath, requestResponse); + requestResponse = FileValidation.ValidateFileExists(request.FullPdfPath, requestResponse); + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/HtmlToPdfService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/HtmlToPdfService.cs new file mode 100644 index 0000000..60fa7ba --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/HtmlToPdfService.cs @@ -0,0 +1,193 @@ +using System; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Serilog; +using Newtonsoft.Json; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.FileOperationsModels.Models.HtmlToPdfModels; +using LCU.Api.Internal.FileOperations.Configurations; +using System.Threading; +namespace LCU.Api.Internal.FileOperations.Services.PdfServices; + +public class HtmlToPdfService : IHtmlToPdfService +{ + private readonly ApiConfiguration _apiConfiguration; + private readonly IDirectFileService _directFileService; + + public HtmlToPdfService(ApiConfiguration apiConfiguration, IDirectFileService directFileService) + { + _apiConfiguration = apiConfiguration; + _directFileService = directFileService; + } + + public byte[] ConvertHtmlToPdf(HtmlToPdfRequest htmlToPdfRequest) + { + try + { + var output = CreateHtmlOutput(htmlToPdfRequest); + var finalOutput = CleanUpUnusedTagsFromOutput(output); + + File.WriteAllText($"{htmlToPdfRequest.FinalHtmlSaveFilePath}{htmlToPdfRequest.PdfName}.html", finalOutput); + var fileCreated = CreatePdfFromHtml(htmlToPdfRequest); + + if (!fileCreated) + { + throw new ServiceException($"Pdf file was not created. HtmlToPdfRequest: {JsonConvert.SerializeObject(htmlToPdfRequest)}"); + } + + var pdfBytes = _directFileService.GetFileBytes(htmlToPdfRequest.FinalPdfSaveFilePathWithFile); + + CleanUpHtmlAndPdfFiles(htmlToPdfRequest); + + return pdfBytes; + } + catch (Exception exception) + { + var message = "Failure to Convert Html To Pdf."; + Log.Error(exception, $"{message} HtmlToPdfRequest:{JsonConvert.SerializeObject(htmlToPdfRequest)}"); + throw new ServiceException($"{message}. ExceptionMessage:{exception.Message}"); + } + } + + public string CreateHtmlOutput(HtmlToPdfRequest htmlToPdfRequest) + { + var templateOutput = AddBodyValuesToTemplateOutput(htmlToPdfRequest); + templateOutput = BuildOutputFromCollectionValues(htmlToPdfRequest.BodyValueCollections, templateOutput); + return templateOutput; + } + + public string AddBodyValuesToTemplateOutput(HtmlToPdfRequest htmlToPdfRequest) + { + var templateOutput = File.ReadAllText(htmlToPdfRequest.HtmlTemplateFilePath); + templateOutput = AddTagValuesToOutput(templateOutput, htmlToPdfRequest.BodyTagValues); + return templateOutput; + } + + public string BuildOutputFromCollectionValues(List valueCollections, string output) + { + if (valueCollections == null || !valueCollections.Any()) + { + return output; + } + + foreach (var bodyValueCollection in valueCollections) + { + var rowTemplateOutput = File.ReadAllText(bodyValueCollection.CollectionHtmlTemplateFilePath); + var rowsBuilder = new StringBuilder(); + rowsBuilder = AddCollectionItemsToStringBuilder(bodyValueCollection.ValueCollectionItems, rowTemplateOutput, rowsBuilder); + output = output.Replace($"{{{bodyValueCollection.CollectionTag}}}", rowsBuilder.ToString()); + } + return output; + } + + public string AddTagValuesToOutput(string output, List tagValues) + { + foreach (var tagValue in tagValues) + { + output = output.Replace($"{{{tagValue.Tag}}}", tagValue.Value); + } + return output; + } + + public StringBuilder AddCollectionItemsToStringBuilder(List valueCollectionItems, string output, StringBuilder builder) + { + foreach (var valueCollectionItem in valueCollectionItems) + { + var tempOutput = output; + tempOutput = AddTagValuesToOutput(tempOutput, valueCollectionItem.TagValues); + + if (valueCollectionItem.ValueCollections != null && valueCollectionItem.ValueCollections.Any()) + { + tempOutput = BuildItemValueCollectionsOutput(valueCollectionItem.ValueCollections, tempOutput); + } + + builder.Append(tempOutput); + } + return builder; + } + + public string BuildItemValueCollectionsOutput(List valueCollections, string templateOutput) + { + if (valueCollections == null) + { + return templateOutput; + } + + templateOutput = BuildOutputFromCollectionValues(valueCollections, templateOutput); + return templateOutput; + } + + public string CleanUpUnusedTagsFromOutput(string output) + { + var pattern = @"{[A-Za-z0-9]*}"; + var finalOutput = Regex.Replace(output, pattern, string.Empty); + return finalOutput; + } + + public void SaveHtmlToFilePath(HtmlToPdfRequest htmlToPdfRequest, string htmlOutput) + { + + using (StreamWriter outputFile = new StreamWriter($"{htmlToPdfRequest.FinalHtmlSaveFilePath}{htmlToPdfRequest.PdfName}.html")) + { + outputFile.Write(htmlOutput); + } + } + + public bool CreatePdfFromHtml(HtmlToPdfRequest htmlToPdfRequest) + { + try + { + var url = $"{htmlToPdfRequest.FinalHtmlSaveFilePath}{htmlToPdfRequest.PdfName}.html"; + var output = Path.Combine(htmlToPdfRequest.FinalPdfSaveFilePath, $"{htmlToPdfRequest.PdfName}.pdf"); + using (var process = new Process()) + { + process.StartInfo.FileName = _apiConfiguration.ChromePath; + process.StartInfo.Arguments = $"--user-data-dir={_apiConfiguration.FileOperationsChromeProfileFolderPath} " + + $"--headless --disable-gpu --print-to-pdf={output} {url} --no-pdf-header-footer"; + process.Start(); + process.WaitForExit(); + } + + var attempts = 0; + var fileCreated = false; + + while (!fileCreated) + { + fileCreated = File.Exists(htmlToPdfRequest.FinalPdfSaveFilePathWithFile); + attempts++; + + if (attempts > _apiConfiguration.MaxFileCheckAttempts) + { + break; + } + + Thread.Sleep(1000); + } + + return fileCreated; + } + catch (Exception exception) + { + var message = $"Failure to Create Pdf From Html."; + Log.Error(exception, message); + throw new Exception($"{message}. Exception Message: {exception.Message}"); + } + } + + public void CleanUpHtmlAndPdfFiles(HtmlToPdfRequest htmlToPdfRequest) + { + if (!htmlToPdfRequest.KeepHtmlFile) + { + File.Delete(htmlToPdfRequest.FinalHtmlSaveFilePathWithFile); + } + + if (!htmlToPdfRequest.KeepPdfFile) + { + File.Delete(htmlToPdfRequest.FinalPdfSaveFilePathWithFile); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IBatchPdfParserService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IBatchPdfParserService.cs new file mode 100644 index 0000000..6a5e50f --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IBatchPdfParserService.cs @@ -0,0 +1,8 @@ +using LCU.Library.Internal.FileOperationsModels.Models; +namespace LCU.Api.Internal.FileOperations.Services; + +public interface IBatchPdfParserService +{ + RequestResponse ExecuteAction(QueueRequest queueRequest); + RequestResponse IsRequestValid(BatchParsePdfRequest request, RequestResponse requestResponse); +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IHtmlToPdfService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IHtmlToPdfService.cs new file mode 100644 index 0000000..a1c19fb --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IHtmlToPdfService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.FileOperationsModels.Models.HtmlToPdfModels; +namespace LCU.Api.Internal.FileOperations.Services.PdfServices; + +public interface IHtmlToPdfService +{ + byte[] ConvertHtmlToPdf(HtmlToPdfRequest htmlToPdfRequest); +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IPdfParserService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IPdfParserService.cs new file mode 100644 index 0000000..77ae13b --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/IPdfParserService.cs @@ -0,0 +1,9 @@ +using LCU.Library.Internal.FileOperationsModels.Models; + +namespace LCU.Api.Internal.FileOperations.Services +{ + public interface IPdfParserService + { + string ParsePdf(ParsePdfRequest parsePdfRequest); + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/PdfParserService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/PdfParserService.cs new file mode 100644 index 0000000..49f531b --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/PdfServices/PdfParserService.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using System.Text; +using PdfSharp.Pdf; +using PdfSharp.Pdf.IO; +using Serilog; +using Newtonsoft.Json; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.FileOperationsModels.Models; +namespace LCU.Api.Internal.FileOperations.Services.PdfServices; + +public class PdfParserService : IPdfParserService +{ + public string ParsePdf(ParsePdfRequest parsePdfRequest) + { + try + { + var fullPath = Path.Combine(parsePdfRequest.RootPath, parsePdfRequest.PdfName); + var inputDocument = PdfReader.Open(fullPath, PdfDocumentOpenMode.Import); + + + var startIndex = parsePdfRequest.PageStart - 1; + var stopIndex = parsePdfRequest.PageStop - 1; + var outputDocument = new PdfDocument(); + for (int page = startIndex; page <= stopIndex; page++) + { + outputDocument.Version = inputDocument.Version; + outputDocument.Info.Title = inputDocument.Info.Title; + outputDocument.Info.Creator = inputDocument.Info.Creator; + outputDocument.AddPage(inputDocument.Pages[page]); + } + + string name = Path.GetFileNameWithoutExtension(parsePdfRequest.PdfName); + var newPdfName = string.IsNullOrWhiteSpace(parsePdfRequest.ParsedPdfname) ? $"{name}.{parsePdfRequest.PageStart}.pdf" : parsePdfRequest.ParsedPdfname; + var newPath = Path.Combine(parsePdfRequest.RootPath, newPdfName); + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + outputDocument.Save(newPath); + + return newPdfName; + } + catch (Exception exception) + { + Log.Error(exception, $"Failure to Parse Pdf. ParsePdfRequest:{JsonConvert.SerializeObject(parsePdfRequest)}"); + throw new ServiceException($"Failure to Parse Pdf. ExceptionMessage:{exception.Message}"); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/QueueServices/IQueueFileOperationsService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/QueueServices/IQueueFileOperationsService.cs new file mode 100644 index 0000000..96f3dcf --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/QueueServices/IQueueFileOperationsService.cs @@ -0,0 +1,10 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models; +using System.Threading.Tasks; +namespace LCU.Api.Internal.FileOperations.Services.QueueServices; + +public interface IQueueFileOperationsService +{ + Task QueueFileOperations(TRequest originalRequest, FileOperationType operationType) where TRequest : ReturnResponse; + Task QueueFileOperationsRequest(QueueRequest queueRequest); +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/QueueServices/QueueFileOperationsService.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/QueueServices/QueueFileOperationsService.cs new file mode 100644 index 0000000..a5fbe7e --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/QueueServices/QueueFileOperationsService.cs @@ -0,0 +1,65 @@ +using System; +using System.Threading.Tasks; +using System.Threading; +using Newtonsoft.Json; +using Serilog; +using MassTransit; +using LCU.Library.Internal.LcuMassTransitModels; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.ApiResponse.Models; +namespace LCU.Api.Internal.FileOperations.Services.QueueServices; + +public class QueueFileOperationsService : IQueueFileOperationsService +{ + private readonly ISendEndpoint _sendEndpoint; + private readonly ReceiveEndpointConfiguration _receiveEndpointConfiguration; + + public QueueFileOperationsService(IBusControl busControl, + ReceiveEndpointConfiguration receiveEndpointConfiguration) + { + _receiveEndpointConfiguration = receiveEndpointConfiguration; + _sendEndpoint = busControl.GetSendEndpoint(_receiveEndpointConfiguration.ReceiveEndpointUri).Result; + } + + public async Task QueueFileOperations(TRequest originalRequest, FileOperationType operationType) where TRequest : ReturnResponse + { + try + { + QueueRequest queueRequest = new QueueRequest() + { + ReturnResponse = originalRequest, + FileOperationType = operationType, + Request = JsonConvert.SerializeObject(originalRequest), + Attempts = 0, + QueueTimeStamp = DateTime.Now + }; + + await QueueFileOperationsRequest(queueRequest); + } + catch (Exception ex) + { + Log.Error(ex, $"Failed to Queue File Operations. FileOperationType: {operationType.ToString()}"); + throw new ServiceException($"Failed to Queue File Operation Request. FileOperationType: {operationType.ToString()}"); + } + } + + public async Task QueueFileOperationsRequest(QueueRequest queueRequest) + { + try + { + var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + await _sendEndpoint.Send(queueRequest, cancellationTokenSource.Token); + } + catch (TaskCanceledException exception) + { + Log.Error(exception, $"Failed to Queue File Operation Request. FileOperationType: {queueRequest.FileOperationType}"); + throw new ServiceException(exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, $"Failed to Queue File Operation Request. FileOperationType: {queueRequest.FileOperationType}"); + throw new ServiceException($"Failed to Queue File Operation Request. FileOperationType: {queueRequest.FileOperationType}"); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/RequestResponseHelper.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/RequestResponseHelper.cs new file mode 100644 index 0000000..9ac2c90 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Services/RequestResponseHelper.cs @@ -0,0 +1,16 @@ +using LCU.Library.Internal.FileOperationsModels.Models; +namespace LCU.Api.Internal.FileOperations.Services; + +public static class RequestResponseHelper +{ + public static RequestResponse ExceptionUpdateRequestResponse(RequestResponse requestResponse, + FileOperationType fileOperationType, + string exception) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add($"Failure to execute file action. " + + $"FileOperationType: {fileOperationType}, " + + $"Exception: {exception} "); + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/DirectoryValidation.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/DirectoryValidation.cs new file mode 100644 index 0000000..703f635 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/DirectoryValidation.cs @@ -0,0 +1,108 @@ +using Serilog; +using System; +using System.IO; +using System.Collections.Generic; +using LCU.Library.Internal.FileOperationsModels.Models; +using System.Linq; +namespace LCU.Api.Internal.FileOperations.Validations; + +public class DirectoryValidation +{ + private const string DirectoryDoesNotExist = "Directory does not exist at path"; + private const string DirectoryExist = "Directory exists at path"; + private const string DirectoriesNotWithinRange = "Number of expected Directories are not within range."; + + public static RequestResponse ValidateDirectoryExists(string path, RequestResponse requestResponse) + { + bool doesDirectoryExists = false; + + try + { + doesDirectoryExists = Directory.Exists(path); + } + catch (Exception exception) + { + Log.Error(exception, $"{DirectoryDoesNotExist} {path}"); + } + + if (!doesDirectoryExists) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add($"{DirectoryDoesNotExist} {path}"); + } + + return requestResponse; + } + + public static RequestResponse ValidateDirectoryNotExists(string path, RequestResponse requestResponse) + { + bool directoryExists = false; + + try + { + directoryExists = !Directory.Exists(path); + } + catch (Exception exception) + { + Log.Error(exception, $"{DirectoryExist} {path}"); + } + + if (!directoryExists) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add($"{DirectoryExist} {path}"); + } + + return requestResponse; + } + + public static RequestResponse ValidateDirectoryRequestsWithinLimit(List requests, + int maxAmountOfDirectories, + RequestResponse requestResponse) + { + bool countWithRange = false; + try + { + countWithRange = requests.Count >= 0 && requests.Count <= maxAmountOfDirectories; + } + catch (Exception exception) + { + var message = $"Unexpected issue while validate directory requests within limit."; + Log.Error(exception, $"Unexpected issue while validate directory requests within limit."); + requestResponse.MessageStack.Add($"{message} Exception Message: {exception.Message}"); + } + + if (!countWithRange) + { + requestResponse.IsSuccessful = countWithRange; + requestResponse.MessageStack.Add($"{DirectoriesNotWithinRange} Expected Range: 0-{maxAmountOfDirectories}, Actual Directories: {requests.Count} "); + } + + return requestResponse; + } + + public static RequestResponse ValidateDirectoryIsEmpty(string path, RequestResponse requestResponse) + { + bool directoryIsEmpty = false; + + try + { + directoryIsEmpty = !Directory.EnumerateFileSystemEntries(path).Any(); + } + catch (Exception exception) + { + + var message = $"Unexpected issue while validate directory is empty."; + Log.Error(exception, message); + requestResponse.MessageStack.Add($"{message} Exception Message: {exception.Message}"); + } + + if(!directoryIsEmpty) + { + requestResponse.IsSuccessful = directoryIsEmpty; + requestResponse.MessageStack.Add($"{path} is not empty."); + } + + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/FileValidation.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/FileValidation.cs new file mode 100644 index 0000000..9f06f21 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/FileValidation.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Serilog; +using LCU.Library.Internal.FileOperationsModels.Models; + +namespace LCU.Api.Internal.FileOperations.Validations; + +public static class FileValidation +{ + private const string DestinationField = "Destination"; + private const string SourceField = "Source"; + private const string FilenameField = "Filename"; + private const string FieldEmptyResponse = " is empty and is required."; + private const string FileNameInvalid = "File name is invalid."; + private const string FileDoesNotExists = "File does not exist at path"; + private const string FileExists = "File exists at path"; + private const string FilesDoNotExists = "Files do not exist at path"; + private const string FilesNotWithinRange = "Number of expected Files are not within range."; + private const string DirectoryDoesNotExists = "Directory does not exists at path"; + private const string DirectoryExist = "Directory exists at path"; + + public static RequestResponse ValidateSourceNotEmpty(string source, RequestResponse requestResponse) + { + requestResponse = ValidateNotEmpty(source, SourceField, requestResponse); + return requestResponse; + } + + public static RequestResponse ValidateDestinationNotEmpty(string destination, RequestResponse requestResponse) + { + requestResponse = ValidateNotEmpty(destination, DestinationField, requestResponse); + return requestResponse; + } + + public static RequestResponse ValidateFilenameNotEmpty(string filename, RequestResponse requestResponse) + { + requestResponse = ValidateNotEmpty(filename, FilenameField, requestResponse); + return requestResponse; + } + + private static RequestResponse ValidateNotEmpty(string field, string fieldName, RequestResponse requestResponse) + { + if (string.IsNullOrEmpty(field)) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add($"{fieldName}{FieldEmptyResponse}"); + } + + return requestResponse; + } + + public static RequestResponse ValidateFilename(string filename, RequestResponse requestResponse) + { + if (filename.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add(FileNameInvalid); + } + return requestResponse; + } + + public static RequestResponse ValidateFileExists(string path, RequestResponse requestResponse) + { + bool doesFileExists = false; + + try + { + doesFileExists = File.Exists(path); + } + catch (Exception exception) + { + Log.Error(exception, $"{FileDoesNotExists} {path}"); + } + + if (!doesFileExists) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add($"{FileDoesNotExists} {path}"); + } + + return requestResponse; + } + + public static RequestResponse ValidateFileNotExists(string path, RequestResponse requestResponse) + { + bool doesFileExists = false; + + try + { + doesFileExists = File.Exists(path); + } + catch (Exception exception) + { + Log.Error(exception, $"{FileExists} {path}"); + } + + if (doesFileExists) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add($"{FileExists} {path}"); + } + + return requestResponse; + } + + public static RequestResponse ValidateIfAnyRequest(List requests, string path, RequestResponse requestResponse) + { + bool anyRequest = false; + + try + { + anyRequest = requests.Any(); + } + catch (Exception exception) + { + Log.Error(exception, $"{FilesDoNotExists} {path}"); + } + + if (!anyRequest) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add($"{FilesDoNotExists} {path}"); + } + + return requestResponse; + } + + public static RequestResponse ValidateIfAnyRequest(List requests, + string path, + bool succeedOnError, + RequestResponse requestResponse) + { + bool anyRequest = false; + try + { + anyRequest = requests.Any(); + } + catch (Exception exception) + { + Log.Error(exception, $"{FilesDoNotExists} {path}"); + } + if (!anyRequest) + { + requestResponse.IsSuccessful = succeedOnError; + requestResponse.MessageStack.Add($"{FilesDoNotExists} {path}"); + } + return requestResponse; + } + + public static RequestResponse ValidateFileRequestsWithinLimit(List requests, + int maxAmountOfFiles, + RequestResponse requestResponse) + { + bool countWithRange = false; + try + { + if (maxAmountOfFiles == 0) + { + return requestResponse; + } + countWithRange = requests.Count >= 0 && requests.Count <= maxAmountOfFiles; + } + catch (Exception exception) + { + var message = $"Unexpected issue while validate file requests within limit."; + Log.Error(exception, $"Unexpected issue while validate file requests within limit."); + requestResponse.MessageStack.Add($"{message} Exception Message: {exception.Message}"); + } + + if (!countWithRange) + { + requestResponse.IsSuccessful = countWithRange; + requestResponse.MessageStack.Add($"{FilesNotWithinRange} Expected Range: 0-{maxAmountOfFiles}, Actual Files: {requests.Count} "); + } + + return requestResponse; + } + + public static RequestResponse ValidateDirectoryExists(string path, RequestResponse requestResponse) + { + bool doesDirectoryExists = false; + + try + { + doesDirectoryExists = Directory.Exists(path); + } + catch (Exception exception) + { + Log.Error(exception, $"{DirectoryDoesNotExists} {path}"); + } + + if (!doesDirectoryExists) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add($"{DirectoryDoesNotExists} {path}"); + } + + return requestResponse; + } + + public static RequestResponse ValidateDirectoryNotExists(string path, RequestResponse requestResponse) + { + bool directoryExists = false; + + try + { + directoryExists = !Directory.Exists(path); + } + catch (Exception exception) + { + Log.Error(exception, $"{DirectoryExist} {path}"); + } + + if (!directoryExists) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add($"{DirectoryExist} {path}"); + } + + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/PdfValidations.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/PdfValidations.cs new file mode 100644 index 0000000..859f255 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/Validations/PdfValidations.cs @@ -0,0 +1,38 @@ +using System; +using PdfSharp.Pdf; +using PdfSharp.Pdf.IO; +using LCU.Library.Internal.FileOperationsModels.Models; +namespace LCU.Api.Internal.FileOperations.Validations; + +public static class PdfValidations +{ + private const string PdfPageDoesntExist = "PDF Page Doesn't Exist."; + private const string PdfPageCantBeLessThanOne = "PDF Page Can't Be Less Than 1."; + + public static RequestResponse ValidatePdfPageExists(RequestResponse requestResponse, string pdfName, int pageNumber) + { + try + { + PdfDocument inputDocument = PdfReader.Open(pdfName, PdfDocumentOpenMode.Import); + + if(pageNumber <= 0) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add(PdfPageCantBeLessThanOne); + } + + if (pageNumber > inputDocument.PageCount) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add(PdfPageDoesntExist); + } + } + catch (Exception exception) + { + requestResponse.IsSuccessful = false; + requestResponse.MessageStack.Add(exception.Message); + } + + return requestResponse; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/appsettings.json b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/appsettings.json new file mode 100644 index 0000000..bbb57be --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/appsettings.json @@ -0,0 +1,122 @@ +{ + "Primary": { + "AppName": "File Operations", + "SeqUrl": "http://localhost:80/", + "SeqApiKey": "hkgw5LWrT7b7hXi3C0RN", + "AttemptsLimit": 2, + //OR Depending on your machine "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" + "ChromePath": "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", + "FileOperationsChromeProfileFolderPath": "C:\\FileOperationsChromeProfile", + "MaxFileCheckAttempts": 60, + "CorsAllowedOrigins": [ + "http://localhost:3000", + "https://localhost:5001", + "http://lcu-internal.com", + "https://lcu-internal.com" + ] + }, + "AllowedHosts": "*", + "ServicesFactoryConfiguration": { + "ServiceFactoryRegistrations": [ + { + "ServiceInterfaceName": "IFileActionService", + "ServiceRegistrations": [ + { + "ServiceName": "MoveFileService", + "ServiceId": "0" + }, + { + "ServiceName": "CopyFileService", + "ServiceId": "1" + }, + { + "ServiceName": "DeleteFileService", + "ServiceId": "2" + }, + { + "ServiceName": "RenameFileService", + "ServiceId": "3" + }, + { + "ServiceName": "ZipDirectoryService", + "ServiceId": "4" + }, + { + "ServiceName": "CreateDirectoryService", + "ServiceId": "5" + }, + { + "ServiceName": "RenameDirectoryService", + "ServiceId": "6" + }, + { + "ServiceName": "BulkOperationService`1", + "ServiceId": "7" + }, + { + "ServiceName": "UnzipFileService", + "ServiceId": "8" + }, + { + "ServiceName": "EncryptFileDataService", + "ServiceId": "9" + }, + { + "ServiceName": "DecryptFileDataService", + "ServiceId": "10" + }, + { + "ServiceName": "ModifyFileService", + "ServiceId": "11" + }, + { + "ServiceName": "UnzipPasswordProtectedFileService", + "ServiceId": "12" + }, + { + "ServiceName": "BatchPdfParserService", + "ServiceId": "13" + }, + { + "ServiceName": "WriteFileService", + "ServiceId": "14" + }, + { + "ServiceName": "DeleteDirectoryService", + "ServiceId": "15" + }, + { + "ServiceName": "ValidateFileService", + "ServiceId": "16" + }, + { + "ServiceName": "AppendFileService", + "ServiceId": "17" + }, + { + "ServiceName": "ExtractIndexFileDataService", + "ServiceId": "18" + }, + { + "ServiceName": "DeleteEmptyDirectoryService", + "ServiceId": "19" + }, + { + "ServiceName": "ValidateFileSizeInRangeService", + "ServiceId": "20" + } + ] + } + ] + }, + "ConnectionConfiguration": { + "HostUrl": "localhost", + "UserName": "guest", + "Password": "guest" + }, + "FileOperationsReceiveEndpoint": { + "ReceiveEndpoint": "LCU.FileOperations", + "ReceiveEndpointType": "exchange", + "PrefetchLimit": 1 + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/libman.json b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/libman.json new file mode 100644 index 0000000..ceee271 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperations/libman.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "defaultProvider": "cdnjs", + "libraries": [] +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/DirectFileOperationsControllerTest.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/DirectFileOperationsControllerTest.cs new file mode 100644 index 0000000..373d23b --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/DirectFileOperationsControllerTest.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Moq; +using Xunit; +using LCU.Api.Internal.FileOperations.Controllers; +using LCU.Api.Internal.FileOperations.Services; +using LCU.Api.Internal.FileOperations.Services.FileActionServices; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.Exceptions; +namespace LCU.Api.Internal.FileOperationsTests.Controllers; + +public class DirectFileOperationsControllerTest +{ + private readonly DirectFileOperationsController _controller; + private readonly Mock _mockDirectFileOperationsService; + private readonly Mock> _mockMoveFileService; + private readonly Mock> _mockWriteFileService; + private readonly Mock> _mockCreateDirectoryService; + private readonly Mock> _mockUnzipFileService; + private readonly Mock> _mockZipDirectoryService; + private readonly Mock> _mockDeleteDirectoryService; + private readonly Mock> _mockDeleteFileService; + private readonly Mock> _mockAppendFileService; + + public DirectFileOperationsControllerTest() + { + _mockDirectFileOperationsService = new Mock(); + _mockMoveFileService = new Mock>(); + _mockWriteFileService = new Mock>(); + _mockCreateDirectoryService = new Mock>(); + _mockUnzipFileService = new Mock>(); + _mockZipDirectoryService = new Mock>(); + _mockDeleteDirectoryService = new Mock>(); + _mockDeleteFileService = new Mock>(); + _mockAppendFileService = new Mock>(); + _controller = new DirectFileOperationsController(_mockDirectFileOperationsService.Object, + _mockMoveFileService.Object, + _mockWriteFileService.Object, + _mockCreateDirectoryService.Object, + _mockUnzipFileService.Object, + _mockZipDirectoryService.Object, + _mockDeleteDirectoryService.Object, + _mockDeleteFileService.Object, + _mockAppendFileService.Object); + } + + [Fact] + public async Task MoveFilePost_OK() + { + // arrange + var request = new MoveFileRequest() + { + SourcePath = "", + SourceFile = "", + DestinationPath = "" + }; + + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockMoveFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + _mockDirectFileOperationsService.Setup(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.Move)) + .ReturnsAsync(validRequestResponse) + .Verifiable(); + + // act + var result = await _controller.MoveFile(request); + Assert.NotNull(result); + var okResult = result as OkObjectResult; + + // assert + Assert.NotNull(okResult); + Assert.Equal(200, okResult.StatusCode); + _mockMoveFileService.Verify(service => + service.PreQueueValidation(It.IsAny()), Times.Once); + _mockDirectFileOperationsService.Verify(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.Move), Times.Once); + } + + [Fact] + public async Task MoveFilePost_InvalidRequest() + { + // arrange + var request = new MoveFileRequest() { }; + var validRequestResponse = new RequestResponse() { MessageStack = new List() { "Invalid" }, IsSuccessful = false }; + _mockMoveFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + // act + var result = await _controller.MoveFile(request); + + Assert.NotNull(result); + var badRequest = result as BadRequestObjectResult; + Assert.Equal(400, badRequest.StatusCode); + Assert.Equal($"Request is invalid. Response: {String.Join("\n ", validRequestResponse.MessageStack.ToArray())}", badRequest.Value); + _mockMoveFileService.Verify(service => + service.PreQueueValidation(It.IsAny()), Times.Once); + _mockDirectFileOperationsService.Verify(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.Move), Times.Never); + } + + [Fact] + public async Task MoveFile_Failure_UnexpectedException() + { + var request = new MoveFileRequest() + { + SourcePath = "", + SourceFile = "", + DestinationPath = "" + }; + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockMoveFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse).Verifiable(); + _mockDirectFileOperationsService.Setup(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.Move)) + .ThrowsAsync(new Exception("Thrown From Unit Test")) + .Verifiable(); + + + + var result = await _controller.MoveFile(request); + Assert.NotNull(result); + + var statusResult = result as StatusCodeResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockMoveFileService.Verify(service => + service.PreQueueValidation(It.IsAny()), Times.Once); + _mockDirectFileOperationsService.Verify(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.Move), Times.Once); + } + + [Fact] + public async Task MoveFile_Failure_ServiceException() + { + var request = new MoveFileRequest() + { + SourcePath = "", + SourceFile = "", + DestinationPath = "" + }; + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockMoveFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse).Verifiable(); + _mockDirectFileOperationsService.Setup(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.Move)) + .ThrowsAsync(new ServiceException("Thrown From Unit Test")) + .Verifiable(); + + + + var result = await _controller.MoveFile(request); + Assert.NotNull(result); + + var statusResult = result as ObjectResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockMoveFileService.Verify(service => + service.PreQueueValidation(It.IsAny()), Times.Once); + _mockDirectFileOperationsService.Verify(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.Move), Times.Once); + } + + [Fact] + public async Task AppendFilePost_Ok() + { + var request = new AppendFileRequest() + { + FileData = "Text to write", + DestinationPath = "SomePath", + Extension = ".txt", + FileName = "FileName" + }; + + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockAppendFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + _mockDirectFileOperationsService.Setup(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.AppendFile)) + .ReturnsAsync(validRequestResponse) + .Verifiable(); + + // act + var result = await _controller.AppendFile(request); + Assert.NotNull(result); + var okResult = result as OkObjectResult; + + // assert + Assert.NotNull(okResult); + Assert.Equal(200, okResult.StatusCode); + _mockAppendFileService.Verify(service => + service.PreQueueValidation(It.IsAny()), Times.Once); + _mockDirectFileOperationsService.Verify(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.AppendFile), Times.Once); + } + + [Fact] + public async Task AppendFilePost_InvalidRequest() + { + // arrange + var request = new AppendFileRequest() { }; + var validRequestResponse = new RequestResponse() { MessageStack = new List() { "Invalid" }, IsSuccessful = false }; + _mockAppendFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + // act + var result = await _controller.AppendFile(request); + + Assert.NotNull(result); + var badRequest = result as BadRequestObjectResult; + Assert.Equal(400, badRequest.StatusCode); + Assert.Equal($"Request is invalid. Response: {String.Join("\n ", validRequestResponse.MessageStack.ToArray())}", badRequest.Value); + _mockAppendFileService.Verify(service => + service.PreQueueValidation(It.IsAny()), Times.Once); + _mockDirectFileOperationsService.Verify(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.AppendFile), Times.Never); + } + + [Fact] + public async Task AppendFile_Failure_ServiceException() + { + var request = new AppendFileRequest() + { + FileData = "Text to write", + DestinationPath = "SomePath", + Extension = ".txt", + FileName = "FileName" + }; + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockAppendFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse).Verifiable(); + _mockDirectFileOperationsService.Setup(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.AppendFile)) + .ThrowsAsync(new ServiceException("Thrown From Unit Test")) + .Verifiable(); + + + + var result = await _controller.AppendFile(request); + Assert.NotNull(result); + + var statusResult = result as ObjectResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockAppendFileService.Verify(service => + service.PreQueueValidation(It.IsAny()), Times.Once); + _mockDirectFileOperationsService.Verify(service => + service.HandleDirectFileOperationsRequest(It.IsAny(), FileOperationType.AppendFile), Times.Once); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/FileOperationsControllerTest.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/FileOperationsControllerTest.cs new file mode 100644 index 0000000..0b838e8 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/FileOperationsControllerTest.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Moq; +using Xunit; +using LCU.Api.Internal.FileOperations.Controllers; +using LCU.Api.Internal.FileOperations.Services; +using LCU.Api.Internal.FileOperations.Services.FileActionServices; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Api.Internal.FileOperations.Services.QueueServices; +namespace LCU.Api.Internal.FileOperationsTests.Controllers; + +public class FileOperationsControllerTest +{ + private readonly FileOperationsController _controller; + private readonly Mock _mockFileOperationsService; + private readonly Mock> _mockMoveFileService; + private readonly Mock> _mockCopyFileService; + private readonly Mock> _mockModifyFileService; + private readonly Mock> _mockDeleteFileService; + private readonly Mock> _mockRenameFileService; + private readonly Mock> _mockZipDirectoryService; + private readonly Mock> _mockUnzipFileService; + private readonly Mock> _mockUnzipPasswordProtectedFileService; + private readonly Mock> _mockCreateDirectoryService; + private readonly Mock> _mockRenameDirectoryService; + private readonly Mock>> _mockBulkMoveFileService; + private readonly Mock> _mockEncryptFileDataService; + private readonly Mock> _mockDecryptFileDataService; + private readonly Mock> _mockWriteFileDataService; + private readonly Mock> _mockDeleteDirectoryService; + private readonly Mock> _mockDeleteEmptyDirectoriesService; + private readonly Mock _mockDirectFileService; + + public FileOperationsControllerTest() + { + _mockFileOperationsService = new Mock(); + _mockMoveFileService = new Mock>(); + _mockCopyFileService = new Mock>(); + _mockModifyFileService = new Mock>(); + _mockDeleteFileService = new Mock>(); + _mockRenameFileService = new Mock>(); + _mockZipDirectoryService = new Mock>(); + _mockUnzipFileService = new Mock>(); + _mockUnzipPasswordProtectedFileService = new Mock>(); + _mockCreateDirectoryService = new Mock>(); + _mockRenameDirectoryService = new Mock>(); + _mockBulkMoveFileService = new Mock>>(); + _mockEncryptFileDataService = new Mock>(); + _mockDecryptFileDataService = new Mock>(); + _mockWriteFileDataService = new Mock>(); + _mockDeleteDirectoryService = new Mock>(); + _mockDeleteEmptyDirectoriesService = new Mock>(); + _mockDirectFileService = new Mock(); + + _controller = new FileOperationsController( + _mockFileOperationsService.Object, + _mockMoveFileService.Object, + _mockCopyFileService.Object, + _mockModifyFileService.Object, + _mockDeleteFileService.Object, + _mockRenameFileService.Object, + _mockZipDirectoryService.Object, + _mockUnzipFileService.Object, + _mockUnzipPasswordProtectedFileService.Object, + _mockCreateDirectoryService.Object, + _mockRenameDirectoryService.Object, + _mockBulkMoveFileService.Object, + _mockEncryptFileDataService.Object, + _mockDecryptFileDataService.Object, + _mockWriteFileDataService.Object, + _mockDeleteDirectoryService.Object, + _mockDeleteEmptyDirectoriesService.Object, + _mockDirectFileService.Object); + } + + [Fact] + public void GetFile_OK() + { + var bytes = new byte[1]; + _mockDirectFileService.Setup(service => service.GetFileBytes(It.IsAny())).Returns(bytes); + + // act + var result = _controller.GetFile("\\\\fakeit\\file.csv"); + Assert.NotNull(result); + var okResult = result as FileContentResult; + + // assert + Assert.NotNull(okResult); + _mockDirectFileService.Verify(service => service.GetFileBytes(It.IsAny()), Times.Once); + } + + [Fact] + public void GetFile_BadRequest_Pre() + { + var bytes = new byte[1]; + _mockDirectFileService.Setup(service => service.GetFileBytes(It.IsAny())).Returns(bytes); + + // act + var result = _controller.GetFile(" "); + Assert.NotNull(result); + var badResult = result as BadRequestObjectResult; + + // assert + Assert.NotNull(badResult); + Assert.Equal(400, badResult.StatusCode); + _mockDirectFileService.Verify(service => service.GetFileBytes(It.IsAny()), Times.Never); + } + + [Fact] + public void GetFile_BadRequest_Post() + { + _mockDirectFileService.Setup(service => service.GetFileBytes(It.IsAny())) + .Throws(); + + // act + var result = _controller.GetFile("\\\\fakeit\\file.csv"); + Assert.NotNull(result); + var badResult = result as BadRequestObjectResult; + + // assert + Assert.NotNull(badResult); + Assert.Equal(400, badResult.StatusCode); + _mockDirectFileService.Verify(service => service.GetFileBytes(It.IsAny()), Times.Once); + } + + // Post tests + [Fact] + public async Task MoveFilePost_OK() + { + // arrange + var request = new MoveFileRequest() + { + SourcePath = "", + SourceFile = "", + DestinationPath = "" + }; + + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockMoveFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + + // act + var result = await _controller.MoveFile(request); + Assert.NotNull(result); + var okResult = result as OkResult; + + // assert + Assert.NotNull(okResult); + Assert.Equal(200, okResult.StatusCode); + } + + // Post tests + [Fact] + public async Task MoveFilePost_InvalidRequest() + { + // arrange + var request = new MoveFileRequest() { }; + var validRequestResponse = new RequestResponse() { MessageStack = new List() { "Invalid" }, IsSuccessful = false }; + _mockMoveFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + // act + var result = await _controller.MoveFile(request); + + Assert.NotNull(result); + var badRequest = result as BadRequestObjectResult; + Assert.Equal(400, badRequest.StatusCode); + Assert.Equal($"Request is invalid. Response: {String.Join("\n ", validRequestResponse.MessageStack.ToArray())}", badRequest.Value); + } + + // Post tests + [Fact] + public async Task CopyFilePost_OK() + { + // arrange + var request = new CopyFileRequest() + { + SourcePath = "", + SourceFile = "", + DestinationPath = "" + }; + + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockCopyFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + + // act + var result = await _controller.CopyFile(request); + Assert.NotNull(result); + var okResult = result as OkResult; + + // assert + Assert.NotNull(okResult); + Assert.Equal(200, okResult.StatusCode); + } + + // Post tests + [Fact] + public async Task CopyFilePost_InvalidRequest() + { + // arrange + var request = new CopyFileRequest() { }; + var validRequestResponse = new RequestResponse() { MessageStack = new List() { "Invalid" }, IsSuccessful = false }; + _mockCopyFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + // act + var result = await _controller.CopyFile(request); + + Assert.NotNull(result); + var badRequest = result as BadRequestObjectResult; + Assert.Equal(400, badRequest.StatusCode); + Assert.Equal($"Request is invalid. Response: {String.Join("\n ", validRequestResponse.MessageStack.ToArray())}", badRequest.Value); + } + + // Post tests + [Fact] + public async Task UnzipPasswordProtectedFilePost_OK() + { + // arrange + var request = new UnzipPasswordProtectedFileRequest() + { + SourceDirectory = "", + PasswordProtectedZipFileName = "", + Password = "", + DestinationPath = "" + }; + + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockUnzipPasswordProtectedFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + + // act + var result = await _controller.UnzipPasswordProtectedFile(request); + + // assert + Assert.NotNull(result); + Assert.Equal(200, (result as OkResult).StatusCode); + } + + // Post tests + [Fact] + public async Task UnzipPasswordProtectedFilePost_InvalidRequest() + { + // arrange + var request = new UnzipPasswordProtectedFileRequest() { }; + var validRequestResponse = new RequestResponse() { MessageStack = new List() { "Invalid" }, IsSuccessful = false }; + _mockUnzipPasswordProtectedFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + // act + var result = await _controller.UnzipPasswordProtectedFile(request); + + Assert.NotNull(result); + var badRequest = result as BadRequestObjectResult; + Assert.Equal(400, badRequest.StatusCode); + Assert.Equal($"Request is invalid. Response: {String.Join("\n ", validRequestResponse.MessageStack.ToArray())}", badRequest.Value); + } + + // Post tests + [Fact] + public async Task HandleRequestResponsePost_CopyFileRequest_OK() + { + // arrange + var request = new CopyFileRequest() + { + SourcePath = "", + SourceFile = "", + DestinationPath = "" + }; + + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockCopyFileService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + + // act + var result = await _controller.HandleRequestResponse(request, FileOperationType.Copy, _mockCopyFileService.Object); + Assert.NotNull(result); + var okResult = result as OkResult; + + // assert + Assert.NotNull(okResult); + Assert.Equal(200, okResult.StatusCode); + } + + // Post tests + [Fact] + public async Task HandleRequestResponsePost_CreateDirectoryRequest_OK() + { + // arrange + var request = new CreateDirectoryRequest() + { + DirectoryName = "", + DirectoryPath = "" + }; + + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockCreateDirectoryService.Setup(service => service.PreQueueValidation(It.IsAny())).Returns(validRequestResponse); + + // act + var result = await _controller.HandleRequestResponse(request, FileOperationType.CreateDirectory, + _mockCreateDirectoryService.Object); + Assert.NotNull(result); + var okResult = result as OkResult; + + // assert + Assert.NotNull(okResult); + Assert.Equal(200, okResult.StatusCode); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/IndexFileControllerTest.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/IndexFileControllerTest.cs new file mode 100644 index 0000000..a08d250 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/IndexFileControllerTest.cs @@ -0,0 +1,81 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Moq; +using Xunit; +using LCU.Library.Internal.Exceptions; +using LCU.Api.Internal.FileOperations.Controllers; +using LCU.Library.Internal.FileOperationsModels.Models; +using System.Threading.Tasks; +using LCU.Api.Internal.FileOperations.Services.QueueServices; + +namespace LCU.Api.Internal.FileOperationsTests.Controllers +{ + public class IndexFileControllerTest + { + private IndexFileController _controller; + private Mock _mockQueueFileOperationsService; + + public IndexFileControllerTest() + { + _mockQueueFileOperationsService = new Mock(); + _controller = new IndexFileController(_mockQueueFileOperationsService.Object); + } + + [Fact] + public async Task BatchPdfParser_OK() + { + var request = new ExtractIndexFileDataRequest() + { + IndexFilePath = "\\test\test", + IndexFileName = "Test.csv" + }; + _mockQueueFileOperationsService.Setup(service => service.QueueFileOperations(It.IsAny(), It.IsAny())).Verifiable(); + + var result = await _controller.QueueExtractIndexFileData(request); + Assert.NotNull(result); + + var acceptedResult = result as AcceptedResult; + Assert.NotNull(acceptedResult); + Assert.Equal(202, acceptedResult!.StatusCode); + _mockQueueFileOperationsService.Verify(); + } + + [Fact] + public async Task ExtractIndexFileDataRequest_ServiceException_InternalServerError() + { + var request = new ExtractIndexFileDataRequest() + { + IndexFilePath = "\\test\test", + IndexFileName = "Test.csv" + }; + _mockQueueFileOperationsService.Setup(service => service.QueueFileOperations(It.IsAny(), It.IsAny())) + .Throws(new ServiceException("Thrown From Unit Test")).Verifiable(); + + var result = await _controller.QueueExtractIndexFileData(request); + Assert.NotNull(result); + var statusResult = result as ObjectResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockQueueFileOperationsService.Verify(); + } + + [Fact] + public async Task ExtractIndexFileDataRequest_UnexpectedException_InternalServerError() + { + var request = new ExtractIndexFileDataRequest() + { + IndexFilePath = "\\test\test", + IndexFileName = "Test.csv" + }; + _mockQueueFileOperationsService.Setup(service => service.QueueFileOperations(It.IsAny(), It.IsAny())) + .Throws(new Exception("Thrown From Unit Test")).Verifiable(); + + var result = await _controller.QueueExtractIndexFileData(request); + Assert.NotNull(result); + var statusResult = result as StatusCodeResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockQueueFileOperationsService.Verify(); + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/PdfControllerTest.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/PdfControllerTest.cs new file mode 100644 index 0000000..16d4dd0 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Controllers/PdfControllerTest.cs @@ -0,0 +1,330 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Moq; +using Xunit; +using LCU.Library.Internal.Exceptions; +using LCU.Api.Internal.FileOperations.Controllers; +using LCU.Api.Internal.FileOperations.Services; +using LCU.Library.Internal.FileOperationsModels.Models; +using System.Threading.Tasks; +using System.Collections.Generic; +using LCU.Library.Internal.FileOperationsModels.Models.HtmlToPdfModels; +using LCU.Api.Internal.FileOperations.Services.QueueServices; +using LCU.Api.Internal.FileOperations.Services.PdfServices; + +namespace LCU.Api.Internal.FileOperationsTests.Controllers +{ + public class PdfControllerTest + { + private PdfController _pdfController; + private Mock _mockQueueFileOperationsService; + private Mock _mockPdfParserService; + private Mock _mockBatchPdfParserService; + private Mock _mockDirectFileService; + private Mock _mockHtmlToPdfService; + + public PdfControllerTest() + { + _mockQueueFileOperationsService = new Mock(); + _mockPdfParserService = new Mock(); + _mockBatchPdfParserService = new Mock(); + _mockDirectFileService = new Mock(); + _mockHtmlToPdfService = new Mock(); + _pdfController = new PdfController(_mockQueueFileOperationsService.Object, + _mockPdfParserService.Object, + _mockBatchPdfParserService.Object, + _mockDirectFileService.Object, + _mockHtmlToPdfService.Object); + } + + [Fact] + public async Task BatchPdfParser_OK() + { + var batchPdfParserRequest = new BatchParsePdfRequest() + { + RootPath = "\\test\test", + PdfName = "Test.pdf", + BatchPdfs = new List() + { + new BatchPdf + { + PageStart = 1, + PageStop = 2 + } + } + }; + var requestResponse = new RequestResponse + { + IsSuccessful = true + }; + _mockBatchPdfParserService.Setup(service => service.IsRequestValid(batchPdfParserRequest, It.IsAny())) + .Returns(requestResponse).Verifiable(); + _mockQueueFileOperationsService.Setup(service => service.QueueFileOperations(It.IsAny(), It.IsAny())).Verifiable(); + + var result = await _pdfController.QueueBatchPdfParser(batchPdfParserRequest); + Assert.NotNull(result); + + var acceptedResult = result as AcceptedResult; + Assert.NotNull(acceptedResult); + Assert.Equal(202, acceptedResult!.StatusCode); + _mockBatchPdfParserService.Verify(); + _mockQueueFileOperationsService.Verify(); + } + + [Fact] + public async Task BatchParsePdfRequest_ServiceException_InternalServerError() + { + var batchPdfParserRequest = new BatchParsePdfRequest() + { + RootPath = "\\test\test", + PdfName = "Test.pdf", + BatchPdfs = new List() + { + new BatchPdf + { + PageStart = 1, + PageStop = 2 + } + } + }; + var requestResponse = new RequestResponse + { + IsSuccessful = true + }; + _mockBatchPdfParserService.Setup(service => service.IsRequestValid(batchPdfParserRequest, It.IsAny())) + .Returns(requestResponse).Verifiable(); + _mockQueueFileOperationsService.Setup(service => service.QueueFileOperations(It.IsAny(), It.IsAny())) + .Throws(new ServiceException("Thrown From Unit Test")).Verifiable(); + + var result = await _pdfController.QueueBatchPdfParser(batchPdfParserRequest); + Assert.NotNull(result); + var statusResult = result as ObjectResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockBatchPdfParserService.Verify(); + _mockQueueFileOperationsService.Verify(); + } + + [Fact] + public async Task BatchParsePdfRequest_UnexpectedException_InternalServerError() + { + var batchPdfParserRequest = new BatchParsePdfRequest() + { + RootPath = "\\test\test", + PdfName = "Test.pdf", + BatchPdfs = new List() + { + new BatchPdf + { + PageStart = 1, + PageStop = 2 + } + } + }; + var requestResponse = new RequestResponse + { + IsSuccessful = true + }; + _mockBatchPdfParserService.Setup(service => service.IsRequestValid(batchPdfParserRequest, It.IsAny())) + .Returns(requestResponse).Verifiable(); + _mockQueueFileOperationsService.Setup(service => service.QueueFileOperations(It.IsAny(), It.IsAny())) + .Throws(new Exception("Thrown From Unit Test")).Verifiable(); + + var result = await _pdfController.QueueBatchPdfParser(batchPdfParserRequest); + Assert.NotNull(result); + var statusResult = result as StatusCodeResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockBatchPdfParserService.Verify(); + _mockQueueFileOperationsService.Verify(); + } + + [Fact] + public void ParsePdf_OK() + { + var parsePdfRequest = new ParsePdfRequest() + { + RootPath = "\\test\test", + PdfName = "Test.pdf", + PageStart = 1, + PageStop = 2 + }; + var newPdfResponse = "test.pdf"; + _mockPdfParserService.Setup(service => service.ParsePdf(parsePdfRequest)).Returns(newPdfResponse).Verifiable(); + + var result = _pdfController.ParsePdf(parsePdfRequest); + Assert.NotNull(result); + + var okResult = result as OkObjectResult; + Assert.NotNull(okResult); + Assert.Equal(200, okResult!.StatusCode); + _mockPdfParserService.Verify(); + } + + [Fact] + public void ParsePdf_ServiceException_InternalServerError() + { + var parsePdfRequest = new ParsePdfRequest() + { + RootPath = "\\test\test", + PdfName = "Test.pdf", + PageStart = 1, + PageStop = 2 + }; + _mockPdfParserService.Setup(service => service.ParsePdf(parsePdfRequest)) + .Throws(new ServiceException("Thrown From Unit Test")).Verifiable(); + + var result = _pdfController.ParsePdf(parsePdfRequest); + Assert.NotNull(result); + + var statusResult = result as ObjectResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockPdfParserService.Verify(); + } + + [Fact] + public void ParsePdf_UnexpectedException_InternalServerError() + { + var parsePdfRequest = new ParsePdfRequest() + { + RootPath = "\\test\test", + PdfName = "Test.pdf", + PageStart = 1, + PageStop = 2 + }; + _mockPdfParserService.Setup(service => service.ParsePdf(parsePdfRequest)) + .Throws(new Exception("Thrown From Unit Test")).Verifiable(); + + var result = _pdfController.ParsePdf(parsePdfRequest); + Assert.NotNull(result); + + var statusResult = result as StatusCodeResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockPdfParserService.Verify(); + } + + [Fact] + public void GetPdfConent_OK() + { + var pdfPath = "\\\\Test\\PdfPath\\test.pdf"; + var btyes = new byte[23122321]; + _mockDirectFileService.Setup(service => service.GetFileBytes(pdfPath)).Returns(btyes).Verifiable(); + + var result = _pdfController.GetPdfContent(pdfPath); + var okResult = result as FileContentResult; + Assert.NotNull(okResult); + _mockDirectFileService.Verify(); + } + + [Fact] + public void GetPdfConent_UnexpectedException_InternalServerError() + { + var pdfPath = "\\\\Test\\PdfPath\\test.pdf"; + _mockDirectFileService.Setup(service => service.GetFileBytes(pdfPath)) + .Throws(new Exception("Thrown From Unit Test")).Verifiable(); + + var result = _pdfController.GetPdfContent(pdfPath); + Assert.NotNull(result); + + var statusResult = result as StatusCodeResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockPdfParserService.Verify(); + } + + [Fact] + public void GetPdfConent_ArgumentException_InternalServerError() + { + var pdfPath = "\\\\Test\\PdfPath\\test.pdf"; + _mockDirectFileService.Setup(service => service.GetFileBytes(pdfPath)) + .Throws(new ArgumentException("Thrown From Unit Test")).Verifiable(); + + var result = _pdfController.GetPdfContent(pdfPath); + Assert.NotNull(result); + var statusResult = result as BadRequestObjectResult; + Assert.NotNull(statusResult); + Assert.Equal(400, statusResult!.StatusCode); + _mockPdfParserService.Verify(); + } + + [Fact] + public void ConvertHtmlToPdf_OK() + { + var htmlToPdfRequest = new HtmlToPdfRequest() + { + HtmlTemplateFilePath = "\\Test", + BodyTagValues = new List + { + new TagValue + { + Tag = "Test Tag", + Value = "Test Value" + } + } + }; + var btyes = new byte[23122321]; + _mockHtmlToPdfService.Setup(service => service.ConvertHtmlToPdf(htmlToPdfRequest)).Returns(btyes).Verifiable(); + + var result = _pdfController.ConvertHtmlToPdf(htmlToPdfRequest); + var okResult = result as FileContentResult; + Assert.NotNull(okResult); + _mockDirectFileService.Verify(); + } + + [Fact] + public void ConvertHtmlToPdf_ServiceExceptionException_InternalServerError() + { + var htmlToPdfRequest = new HtmlToPdfRequest() + { + HtmlTemplateFilePath = "\\Test", + BodyTagValues = new List + { + new TagValue + { + Tag = "Test Tag", + Value = "Test Value" + } + } + }; + _mockHtmlToPdfService.Setup(service => service.ConvertHtmlToPdf(htmlToPdfRequest)) + .Throws(new ServiceException("Thrown From Unit Test")).Verifiable(); + + var result = _pdfController.ConvertHtmlToPdf(htmlToPdfRequest); + Assert.NotNull(result); + + var statusResult = result as ObjectResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockPdfParserService.Verify(); + } + + [Fact] + public void GetConvertHtmlToPdf_UnexpectedExceptionException_InternalServerError() + { + var htmlToPdfRequest = new HtmlToPdfRequest() + { + HtmlTemplateFilePath = "\\Test", + BodyTagValues = new List + { + new TagValue + { + Tag = "Test Tag", + Value = "Test Value" + } + } + }; + _mockHtmlToPdfService.Setup(service => service.ConvertHtmlToPdf(htmlToPdfRequest)) + .Throws(new Exception("Thrown From Unit Test")).Verifiable(); + + var result = _pdfController.ConvertHtmlToPdf(htmlToPdfRequest); + Assert.NotNull(result); + + var statusResult = result as StatusCodeResult; + Assert.NotNull(statusResult); + Assert.Equal(500, statusResult!.StatusCode); + _mockPdfParserService.Verify(); + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/LCU.Api.Internal.FileOperationsTests.csproj b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/LCU.Api.Internal.FileOperationsTests.csproj new file mode 100644 index 0000000..88e60e2 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/LCU.Api.Internal.FileOperationsTests.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + + false + + + + + + + + + + + + + + + diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/DirectFileOperationsServiceTest.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/DirectFileOperationsServiceTest.cs new file mode 100644 index 0000000..8029d18 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/DirectFileOperationsServiceTest.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Moq; +using Xunit; +using LCU.Api.Internal.FileOperations.Services; +using LCU.Library.Internal.FileOperationsModels.Models; +namespace LCU.Api.Internal.FileOperationsTests.Services; + +public class DirectFileOperationsServiceTest +{ + private readonly DirectFileOperationsService _directFileOperationsService; + private Mock _mockFileOperationsService; + + public DirectFileOperationsServiceTest() + { + _mockFileOperationsService = new Mock(); + _directFileOperationsService = new DirectFileOperationsService(_mockFileOperationsService.Object); + } + + [Fact] + public async Task HandleDirectFileOperationsRequest_Return_IsSuccessful_True() + { + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockFileOperationsService.Setup(service => service.PerformFileOperation(It.IsAny())).Returns(validRequestResponse); + + var request = new MoveFileRequest() + { + SourcePath = "", + SourceFile = "", + DestinationPath = "" + }; + + // act + var result = await _directFileOperationsService.HandleDirectFileOperationsRequest(request, FileOperationType.Move); + Assert.NotNull(result); + Assert.True(validRequestResponse.IsSuccessful); + _mockFileOperationsService.Verify(service => service.PerformFileOperation(It.IsAny()), Times.Once); + } + + [Fact] + public async Task HandleDirectFileOperationsRequest_Return_IsSuccessful_False() + { + var validRequestResponse = new RequestResponse() { MessageStack = new List { "test" }, IsSuccessful = false }; + _mockFileOperationsService.Setup(service => service.PerformFileOperation(It.IsAny())).Returns(validRequestResponse); + + var request = new MoveFileRequest() + { + SourcePath = "", + SourceFile = "", + DestinationPath = "" + }; + + // act + var result = await _directFileOperationsService.HandleDirectFileOperationsRequest(request, FileOperationType.Move); + Assert.NotNull(result); + Assert.False(result.IsSuccessful); + Assert.Single(result.MessageStack); + _mockFileOperationsService.Verify(service => service.PerformFileOperation(It.IsAny()), Times.Once); + } + + [Fact] + public void HandleDirectFileOperationsRequest_PerformFileOperation_ThrowsException() + { + var validRequestResponse = new RequestResponse() { MessageStack = new List(), IsSuccessful = true }; + _mockFileOperationsService.Setup(service => service.PerformFileOperation(It.IsAny())).Throws(new Exception("Test")); + + var request = new MoveFileRequest() + { + SourcePath = "", + SourceFile = "", + DestinationPath = "" + }; + + // act + Assert.ThrowsAsync(async () => await _directFileOperationsService.HandleDirectFileOperationsRequest(request, FileOperationType.Move)); + _mockFileOperationsService.Verify(service => service.PerformFileOperation(It.IsAny()), Times.Once); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/DirectFileServiceTest.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/DirectFileServiceTest.cs new file mode 100644 index 0000000..c02e754 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/DirectFileServiceTest.cs @@ -0,0 +1,27 @@ +using LCU.Api.Internal.FileOperations.Services; +using System; +using Xunit; + +namespace LCU.Api.Internal.FileOperationsTests.Services +{ + public class DirectFileServiceTest + { + private readonly DirectFileService _directFileService; + + public DirectFileServiceTest() + { + _directFileService = new DirectFileService(); + } + + [Fact] + public void Sir_Throws_A_Lot() + { + Assert.Throws( + () => { _directFileService.GetFileBytes(" "); }); + Assert.Throws( + () => { _directFileService.GetFileBytes("C:\\secret.txt"); }); + Assert.Throws( + () => { _directFileService.GetFileBytes("asdf"); }); + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/FileOperationsServiceTest.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/FileOperationsServiceTest.cs new file mode 100644 index 0000000..8afaf65 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/FileOperationsServiceTest.cs @@ -0,0 +1,126 @@ +using Moq; +using Xunit; +using LCU.Api.Internal.FileOperations.Services; +using LCU.Api.Internal.FileOperations.Services.FileActionServices; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Api.Internal.FileOperations.Configurations; +using LCU.Api.Internal.FileOperations.Services.QueueServices; +using LCU.Library.Internal.LCUServiceFactory.Services; +using System; +namespace LCU.Api.Internal.FileOperationsTests.Services; + +public class FileOperationsServiceTest +{ + private readonly FileOperationsService _fileOperationsService; + private readonly Mock> _mockServiceFactory; + private Mock _mockQueueFileOperationsService; + + public FileOperationsServiceTest() + { + var apiConfiguration = new ApiConfiguration() + { + AttemptsLimit = 2 + }; + _mockQueueFileOperationsService = new Mock(); + _mockServiceFactory = new Mock>(); + _fileOperationsService = new FileOperationsService( + _mockQueueFileOperationsService.Object, + apiConfiguration, + _mockServiceFactory.Object); + ; + } + + [Fact] + public void Basic_Construct_Test() + { + Assert.NotNull(_fileOperationsService); + } + + [Fact] + public void ShouldPerformFileOperationsAgain_AttempsLessThenLimit_True() + { + var request = new QueueRequest() + { + Attempts = 0 + }; + var requestResponse = new RequestResponse() + { + IsSuccessful = false + }; + var result = _fileOperationsService.ShouldPerformFileOperationsAgain(request, requestResponse); + + Assert.True(result); + } + + [Fact] + public void ShouldPerformFileOperationsAgain_AttempsGreaterThenLimit_False() + { + var request = new QueueRequest() + { + Attempts = 3 + }; + + var requestResponse = new RequestResponse() + { + IsSuccessful = false + }; + var result = _fileOperationsService.ShouldPerformFileOperationsAgain(request, requestResponse); + + Assert.False(result); + } + + [Fact] + public void PerformFileOperation_IsSuccessful() + { + QueueRequest request = null; + + var result = _fileOperationsService.PerformFileOperation(request); + + Assert.NotNull(result); + Assert.Single(result.MessageStack); + Assert.True(result.IsSuccessful); + } + + [Fact] + public void PerformFileOperation_NullRequest_IsSuccessful() + { + var request = new QueueRequest() + { + Attempts = 3, + FileOperationType = FileOperationType.Move + }; + + var response = new RequestResponse(); + + var mockedFileActionService = new Mock(); + _mockServiceFactory.Setup(x => x.GetService(request.FileOperationType)).Returns(mockedFileActionService.Object); + mockedFileActionService.Setup(x => x.ExecuteAction(request)).Returns(response); + + var result = _fileOperationsService.PerformFileOperation(request); + + Assert.NotNull(result); + Assert.Empty(result.MessageStack); + Assert.True(result.IsSuccessful); + } + + [Fact] + public void PerformFileOperation_ServiceFactoryException_IsNotSuccessful() + { + var request = new QueueRequest() + { + Attempts = 3, + FileOperationType = FileOperationType.Move + }; + + var response = new RequestResponse(); + + var mockedFileActionService = new Mock(); + _mockServiceFactory.Setup(x => x.GetService(request.FileOperationType)).Throws(new Exception("Failure")); + + var result = _fileOperationsService.PerformFileOperation(request); + + Assert.NotNull(result); + Assert.Single(result.MessageStack); + Assert.False(result.IsSuccessful); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/Validations/FileValidationTest.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/Validations/FileValidationTest.cs new file mode 100644 index 0000000..fa974b2 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Services/Validations/FileValidationTest.cs @@ -0,0 +1,128 @@ +using System.Linq; +using Xunit; +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Api.Internal.FileOperations.Validations; +namespace LCU.Api.Internal.FileOperationsTests.Services.Validations; + +public class FileValidationTest +{ + [Fact] + public void IsSourceEmpty_SourceEmpty_ReturnIsValid_False() + { + // arrange + var sourcePath = ""; + var responseRequest = new RequestResponse(); + + // act + responseRequest = FileValidation.ValidateSourceNotEmpty(sourcePath, responseRequest); + + // assert + Assert.False(responseRequest.IsSuccessful); + Assert.Equal("Source is empty and is required.", responseRequest.MessageStack.FirstOrDefault()); + } + + [Fact] + public void IsSourceEmpty_SourceNotEmpty_ReturnIsValid_True() + { + // arrange + var sourcePath = "TESTSource"; + var responseRequest = new RequestResponse(); + + // act + responseRequest = FileValidation.ValidateSourceNotEmpty(sourcePath, responseRequest); + + // assert + Assert.True(responseRequest.IsSuccessful); + Assert.Empty(responseRequest.MessageStack); + } + + [Fact] + public void IsDestinationEmpty_DestinationEmpty_ReturnIsValid_False() + { + // arrange + var sourcePath = ""; + var responseRequest = new RequestResponse(); + + // act + responseRequest = FileValidation.ValidateDestinationNotEmpty(sourcePath, responseRequest); + + // assert + Assert.False(responseRequest.IsSuccessful); + Assert.Equal("Destination is empty and is required.", responseRequest.MessageStack.FirstOrDefault()); + } + + [Fact] + public void IsDestinationEmpty_DestinationNotEmpty_ReturnIsValid_True() + { + // arrange + var destinationPath = "TESTDestination"; + var responseRequest = new RequestResponse(); + + // act + responseRequest = FileValidation.ValidateDestinationNotEmpty(destinationPath, responseRequest); + + // assert + Assert.True(responseRequest.IsSuccessful); + Assert.Empty(responseRequest.MessageStack); + } + + [Fact] + public void IsFilenameEmpty_FilenameEmpty_ReturnIsValid_False() + { + // arrange + var filename = ""; + var responseRequest = new RequestResponse(); + + // act + responseRequest = FileValidation.ValidateFilenameNotEmpty(filename, responseRequest); + + // assert + Assert.False(responseRequest.IsSuccessful); + Assert.Equal("Filename is empty and is required.", responseRequest.MessageStack.FirstOrDefault()); + } + + [Fact] + public void IsFilenameEmpty_FilenameNotEmpty_ReturnIsValid_True() + { + // arrange + var filename = "TESTFilename"; + var responseRequest = new RequestResponse(); + + // act + responseRequest = FileValidation.ValidateDestinationNotEmpty(filename, responseRequest); + + // assert + Assert.True(responseRequest.IsSuccessful); + Assert.Empty(responseRequest.MessageStack); + } + + [Fact] + public void IsFilenameValid_FilenameNotValid_ReturnIsValid_False() + { + // arrange + var filename = "/**"; + var responseRequest = new RequestResponse(); + + // act + responseRequest = FileValidation.ValidateFilename(filename, responseRequest); + + // assert + Assert.False(responseRequest.IsSuccessful); + Assert.Equal("File name is invalid.", responseRequest.MessageStack.FirstOrDefault()); + } + + [Fact] + public void IsFilenameValid_FilenameValid_ReturnIsValid_True() + { + // arrange + var filename = "TESTFilename"; + var responseRequest = new RequestResponse(); + + // act + responseRequest = FileValidation.ValidateFilename(filename, responseRequest); + + // assert + Assert.True(responseRequest.IsSuccessful); + Assert.Empty(responseRequest.MessageStack); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Shared/DynamicNameBuilderTest.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Shared/DynamicNameBuilderTest.cs new file mode 100644 index 0000000..9986505 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Shared/DynamicNameBuilderTest.cs @@ -0,0 +1,144 @@ +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System; +using System.Collections.Generic; +using Xunit; + +namespace LCU.Api.Internal.FileOperationsTests.Shared +{ + public class DynamicNameBuilderTest + { + public DynamicNameBuilderTest() + { + } + + [Fact] + public void EmptyFileNameSettings_Return_FileName() + { + // arrange + var name = "testfile.txt"; + var settings = new DynamicNameSetting(); + + // act + var result = DynamicNameBuilder.BuildDynamicName(name, settings); + + // assert + Assert.Equal(name, result); + } + + [Fact] + public void NullFileNameSettings_Return_FileName() + { + // arrange + var name = "testfile.txt"; + DynamicNameSetting settings = null; + + // act + var result = DynamicNameBuilder.BuildDynamicName(name, settings); + + // assert + Assert.Equal(name, result); + } + + [Fact] + public void Return_FileName_dd_MM() + { + // arrange + var name = "testfile_{TestDate}.txt"; + var settings = new DynamicNameSetting(); + + var dateFormatSetting = new DateFormatSetting(); + dateFormatSetting.Tag = "{TestDate}"; + dateFormatSetting.DateFormat = "dd_MM"; + settings.DateFormatSettings = new List(); + settings.DateFormatSettings.Add(dateFormatSetting); + + // act + var result = DynamicNameBuilder.BuildDynamicName(name, settings); + + // assert + Assert.Equal($"testfile_{DateTime.Now:dd_MM}.txt", result); + } + + [Fact] + public void Minus_One_Day_Return_FileName_dd_MM() + { + // arrange + var name = "testfile_{TestDate}.txt"; + var settings = new DynamicNameSetting(); + settings.DaysFromToday = -1; + + var dateFormatSetting = new DateFormatSetting(); + dateFormatSetting.Tag = "{TestDate}"; + dateFormatSetting.DateFormat = "dd_MM"; + settings.DateFormatSettings = new List(); + settings.DateFormatSettings.Add(dateFormatSetting); + + // act + var result = DynamicNameBuilder.BuildDynamicName(name, settings); + + // assert + Assert.Equal($"testfile_{DateTime.Now.AddDays(-1):dd_MM}.txt", result); + } + + [Fact] + public void Multiple_Formats_sepratedby_dashs_Return_FileName_dd_MM_yyyy() + { + // arrange + var name = "testfile_{TestDate}-{TestDate2}-{TestDate3}.txt"; + var settings = new DynamicNameSetting(); + settings.DateFormatSettings = new List(); + + var dateFormatSetting1 = new DateFormatSetting(); + dateFormatSetting1.Tag = "{TestDate}"; + dateFormatSetting1.DateFormat = "dd"; + settings.DateFormatSettings.Add(dateFormatSetting1); + + var dateFormatSetting2 = new DateFormatSetting(); + dateFormatSetting2.Tag = "{TestDate2}"; + dateFormatSetting2.DateFormat = "MM"; + settings.DateFormatSettings.Add(dateFormatSetting2); + + var dateFormatSetting3 = new DateFormatSetting(); + dateFormatSetting3.Tag = "{TestDate3}"; + dateFormatSetting3.DateFormat = "yyyy"; + settings.DateFormatSettings.Add(dateFormatSetting3); + + // act + var result = DynamicNameBuilder.BuildDynamicName(name, settings); + + // assert + Assert.Equal($"testfile_{DateTime.Now:dd}-{DateTime.Now:MM}-{DateTime.Now:yyyy}.txt", result); + } + + [Fact] + public void Multiple_Formats_nosepration_Return_FileName_ddMMyyyy() + { + // arrange + var fileName = "testfile_{TestDate}{TestDate2}{TestDate3}.txt"; + var settings = new DynamicNameSetting(); + settings.DateFormatSettings = new List(); + + var dateFormatSetting1 = new DateFormatSetting(); + dateFormatSetting1.Tag = "{TestDate}"; + dateFormatSetting1.DateFormat = "dd"; + settings.DateFormatSettings.Add(dateFormatSetting1); + + var dateFormatSetting2 = new DateFormatSetting(); + dateFormatSetting2.Tag = "{TestDate2}"; + dateFormatSetting2.DateFormat = "MM"; + settings.DateFormatSettings.Add(dateFormatSetting2); + + var dateFormatSetting3 = new DateFormatSetting(); + dateFormatSetting3.Tag = "{TestDate3}"; + dateFormatSetting3.DateFormat = "yyyy"; + settings.DateFormatSettings.Add(dateFormatSetting3); + + // act + var result = DynamicNameBuilder.BuildDynamicName(fileName, settings); + + // assert + Assert.Equal($"testfile_{DateTime.Now:dd}{DateTime.Now:MM}{DateTime.Now:yyyy}.txt", result); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Shared/RegexNameHandlerTest.cs b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Shared/RegexNameHandlerTest.cs new file mode 100644 index 0000000..8f9766f --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/Shared/RegexNameHandlerTest.cs @@ -0,0 +1,90 @@ +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.Collections.Generic; +using Xunit; + +namespace LCU.Api.Internal.FileOperationsTests.Shared +{ + public class RegexNameHandlerTest + { + + [Fact] + public void BuildFileList_NoRegex_Return_SingleFileName() + { + // arrange + var name = "testfile.txt"; + var path = "\\TEST"; + var settings = new DynamicNameSetting(); + + // act + var result = RegexNameHandler.BuildFileList(path, name, settings); + + // assert + Assert.Single(result); + } + + [Fact] + public void BuildRegexName_Return_FileName_OneRegex_Replaced() + { + // arrange + var name = "testfile_{Regex01}.txt"; + var settings = new DynamicNameSetting(); + settings.RegexSettings = new List(); + var regexTest = new RegexSetting() + { + Tag = "{Regex01}", + Regex = "\\d*" + }; + settings.RegexSettings.Add(regexTest); + + // act + var result = RegexNameHandler.BuildRegexName(name, settings); + + // assert + Assert.Equal("testfile_\\d*.txt", result.ToString()); + } + + [Fact] + public void BuildRegexName_Return_FileName_TwoRegex_Replaced() + { + // arrange + var name = "testfile_{Regex01}_Test_{Regex02}.txt"; + var settings = new DynamicNameSetting(); + settings.RegexSettings = new List(); + var regexTest1 = new RegexSetting() + { + Tag = "{Regex01}", + Regex = "\\d*" + }; + settings.RegexSettings.Add(regexTest1); + + var regexTest2 = new RegexSetting() + { + Tag = "{Regex02}", + Regex = "\\w*" + }; + settings.RegexSettings.Add(regexTest2); + + // act + var result = RegexNameHandler.BuildRegexName(name, settings); + + // assert + Assert.Equal("testfile_\\d*_Test_\\w*.txt", result.ToString()); + } + + [Fact] + public void ReplaceTagWithRegex_Return_FileName_OneRegex_Replaced() + { + // arrange + var name = "testfile_{Regex01}.txt"; + var tag = "{Regex01}"; + var regex = "\\d*"; + + // act + var result = RegexNameHandler.ReplaceTagWithRegex(name, tag, regex); + + // assert + Assert.Equal("testfile_\\d*.txt", result); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/additional_test_args.runsettings b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/additional_test_args.runsettings new file mode 100644 index 0000000..16d6a41 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Api.Internal.FileOperationsTests/additional_test_args.runsettings @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + .*microsoft.* + + + + + + + + + + + diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Constants/IndexEvaluationRequestVariablesConsts.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Constants/IndexEvaluationRequestVariablesConsts.cs new file mode 100644 index 0000000..3c1769d --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Constants/IndexEvaluationRequestVariablesConsts.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; + +namespace LCU.Library.Internal.FileOperationsModels.Constants; + +public static class IndexEvaluationRequestVariablesConsts +{ + public static readonly List IndexEvaluationRequestVariablesConstValues = new() + { + InputFieldZero, + InputFieldOne, + InputFieldTwo, + InputFieldThree, + InputFieldFour, + InputFieldFive, + InputFieldSix, + InputFieldSeven, + InputFieldEight, + InputFieldNine, + InputFieldTen, + InputFieldEleven, + InputFieldTwelve, + InputFieldThirteen, + InputFieldFourteen, + InputFieldFifteen, + InputFieldSixteen, + InputFieldSeventeen, + InputFieldEighteen, + InputFieldNineteen, + InputFieldTwenty, + InputFieldTwentyOne, + InputFieldTwentyTwo, + InputFieldTwentyThree, + InputFieldTwentyFour, + InputFieldTwentyFive, + InputFieldTwentySix, + InputFieldTwentySeven, + InputFieldTwentyEight, + InputFieldTwentyNine + }; + + public const string InputFieldZero = "InputFieldZero"; + public const string InputFieldOne = "InputFieldOne"; + public const string InputFieldTwo = "InputFieldTwo"; + public const string InputFieldThree = "InputFieldThree"; + public const string InputFieldFour = "InputFieldFour"; + public const string InputFieldFive = "InputFieldFive"; + public const string InputFieldSix = "InputFieldSix"; + public const string InputFieldSeven = "InputFieldSeven"; + public const string InputFieldEight = "InputFieldEight"; + public const string InputFieldNine = "InputFieldNine"; + public const string InputFieldTen = "InputFieldTen"; + public const string InputFieldEleven = "InputFieldEleven"; + public const string InputFieldTwelve = "InputFieldTwelve"; + public const string InputFieldThirteen = "InputFieldThirteen"; + public const string InputFieldFourteen = "InputFieldFourteen"; + public const string InputFieldFifteen = "InputFieldFifteen"; + public const string InputFieldSixteen = "InputFieldSixteen"; + public const string InputFieldSeventeen = "InputFieldSeventeen"; + public const string InputFieldEighteen = "InputFieldEighteen"; + public const string InputFieldNineteen = "InputFieldNineteen"; + public const string InputFieldTwenty = "InputFieldTwenty"; + public const string InputFieldTwentyOne = "InputFieldTwentyOne"; + public const string InputFieldTwentyTwo = "InputFieldTwentyTwo"; + public const string InputFieldTwentyThree = "InputFieldTwentyThree"; + public const string InputFieldTwentyFour = "InputFieldTwentyFour"; + public const string InputFieldTwentyFive = "InputFieldTwentyFive"; + public const string InputFieldTwentySix = "InputFieldTwentySix"; + public const string InputFieldTwentySeven = "InputFieldTwentySeven"; + public const string InputFieldTwentyEight = "InputFieldTwentyEight"; + public const string InputFieldTwentyNine = "InputFieldTwentyNine"; +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/Delimiters.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/Delimiters.cs new file mode 100644 index 0000000..290f8fc --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/Delimiters.cs @@ -0,0 +1,12 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace LCU.Library.Internal.FileOperationsModels.Enums; + +public enum Delimiters +{ + [Display(Name = ",")] + Comma, + [Display(Name = "|")] + Pipe +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/FileSizeTypes.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/FileSizeTypes.cs new file mode 100644 index 0000000..4bdf29b --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/FileSizeTypes.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.Library.Internal.FileOperationsModels.Enums; + +public enum FileSizeTypes +{ + [Display(Name = "bytes")] + Byte = 0, + [Display(Name = "KB")] + Kilobyte = 1, + [Display(Name = "MB")] + Megabyte = 2, + [Display(Name = "GB")] + Gigabyte = 3, + [Display(Name = "TB")] + Terabyte = 4 +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/OperatorTypes.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/OperatorTypes.cs new file mode 100644 index 0000000..7638aca --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Enums/OperatorTypes.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; +namespace LCU.Library.Internal.FileOperationsModels.Enums; + +public enum OperatorTypes +{ + [Display(Name = "=")] + Equals = 0, + [Display(Name = ">")] + GreaterThan = 1, + [Display(Name = "=>")] + GreaterThanEquals = 2, + [Display(Name = "<")] + LessThan = 3, + [Display(Name = "<=")] + LessThanEquals = 4 +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/LCU.Library.Internal.FileOperationsModels.csproj b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/LCU.Library.Internal.FileOperationsModels.csproj new file mode 100644 index 0000000..2201f24 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/LCU.Library.Internal.FileOperationsModels.csproj @@ -0,0 +1,119 @@ + + + + netstandard2.0; netstandard2.1; net6.0; net8.0 + true + latest + 4.1.5 + + 2.0.0 - Addition of ReturnResponse and QueueReturnResponse from the LCU.Library.Internal.ApiResponse + 2.1.0 - Upgrade of ApiResponse and QueueLib Version + 2.2.0 - Addition of ZipDirectoryRequest + 3.0.0 - Creation of FileNameSettings and adding it to copy, delete, move, and rename models. + 3.1.0 - Added CreateDirectory and RenameDirectory request. + 3.2.0 - Addition of the RegexSettings for the Copy, Delete, and Move file operation. + 3.3.0 - Added NameSettings for RenameFile NewFileName field. + 3.4.0 - Fix to check for no Regex settings. Addition of NoFilesExist validation. + 3.6.0 - New BulkOperationsRequest + 3.7.0 - MoveFileRequest and CopyFileRequest now use FileRequest. Added generic method to BuildListOfRequest + 3.7.1 - Added DynamicfileListRequest model. + 3.8.0 - Upgrade of ApiResponse and QueueLib. + 3.9.0 - New UnzipFile functionality. + 3.10.0 - Dynamic Zip and Unzip Functionality. + 3.11.0 - Add File Data Encryption/Decryption Actions. + 3.12.0 - Add SucceedOnError to Copy/Rename/Move/Delete File + 3.13.0 - Additions to RequestResponse, Added netstandard2.1 and net6.0, fix for regex directory name builder, and changed validation to return RequestResponse + 3.14.0 - Upgrade ApiResponse Models Library + 3.15.0 - Upgrade ApiResponse to use newest models version. With queueName obsolete. + 3.16.0 - Add Modify File Model. + 3.17.0 - Addition of ParsePdfRequest and moving FileRequestValidation to Obsolete as it should not be used by outside apis. + 3.18.0 - New UnzipPasswordProtectedFile functionality. + 3.19.0 - Add DynamicDate Source/Destination paths for Unzip and UnzipPasswordProtected. Added BuildDynamicDirectoryList functionality. + 3.20.0 - Add UseZipFileNameAsFinalDestination Property to the UnzipFileRequest model + 3.21.0 - Add BatchParsePdfRequest + 3.22.0 - Add WriteFileRequest + 3.23.0 - Add DeleteDirectoryRequest + 3.24.0 - Alter ModifyFile to change extensions + 3.25.0 - Add ValidateFileRequest + 3.26.0 - Removed SucceedOnError field from ValidateFileRequest + 3.27.0 - Added values to UnzipFileRequest model to handle flattening file structure + 3.28.0 - Added ExcelModels + 3.29.0 - ExcelModels modifications for the use of OpenXml enhancment + 3.30.0 - Added MaxAmountOfFiles field to ModifyFileRequest + 3.30.1 - Added HtmlToPdfModels + 3.30.2 - Add AppendFile + 3.30.3 - Add ExtractIndexFileData + 3.30.4 - Edit IndexEvaluationRequest + 3.30.5 - Upgrade NuGets + 3.30.6 - Change ExcelDataRequest.FilePath to ExcelDataRequest.FileContent + 3.31.0 - Upgrade to dotnet8 + 4.0.0 - Updated project name + 4.1.1 - Added DeleteEmptyDirectoryRequest + 4.1.2 - Added ValidateFizeSizeInRangeRequest and FileSiseRequest + 4.1.3 - Update FileSourceRequest + 4.1.4 - Update ValidateFileRequest to add CheckFileCount Property and added new enum OperatorTypes + 4.1.5 - QueueRequest update to add ReturnResponse + + + 2.0.0 - Addition of ReturnResponse and QueueReturnResponse from the LCU.Library.Internal.ApiResponse + 2.1.0 - Upgrade of ApiResponse and QueueLib Version + 2.2.0 - Addition of ZipDirectoryRequest + 3.0.0 - Creation of FileNameSettings and adding it to copy, delete, move, and rename models. + 3.1.0 - Added CreateDirectory and RenameDirectory request. + 3.2.0 - Addition of the RegexSettings for the Copy, Delete, and Move file operation. + 3.3.0 - Added NameSettings for RenameFile NewFileName field. + 3.4.0 - Fix to check for no Regex settings. Addition of NoFilesExist validation. + 3.6.0 - New BulkOperationsRequest + 3.7.0 - MoveFileRequest and CopyFileRequest now use FileRequest. Added generic method to BuildListOfRequest + 3.7.1 - Added DynamicfileListRequest model. + 3.8.0 - Upgrade of ApiResponse and QueueLib. + 3.9.0 - New UnzipFile functionality. + 3.10.0 - Dynamic Zip and Unzip Functionality. + 3.11.0 - Add File Data Encryption/Decryption Actions. + 3.12.0 - Add SucceedOnError to Copy/Rename/Move/Delete File + 3.13.0 - Additions to RequestResponse, Added netstandard2.1 and net6.0, fix for regex directory name builder, and changed validation to return RequestResponse + 3.14.0 - Upgrade ApiResponse Models Library + 3.15.0 - Upgrade ApiResponse to use newest models version. With queueName obsolete. + 3.16.0 - Add Modify File Model. + 3.17.0 - Addition of ParsePdfRequest and moving FileRequestValidation to Obsolete as it should not be used by outside apis. + 3.18.0 - New UnzipPasswordProtectedFile functionality. + 3.19.0 - Add DynamicDate Source/Destination paths for Unzip and UnzipPasswordProtected. Added BuildDynamicDirectoryList functionality. + 3.20.0 - Add UseZipFileNameAsFinalDestination Property to the UnzipFileRequest model + 3.21.0 - Add BatchParsePdfRequest + 3.22.0 - Add WriteFileRequest + 3.23.0 - Add DeleteDirectoryRequest + 3.24.0 - Alter ModifyFile to change extensions + 3.25.0 - Add ValidateFileRequest + 3.26.0 - Removed SucceedOnError field from ValidateFileRequest + 3.27.0 - Added values to UnzipFileRequest model to handle flattening file structure + 3.28.0 - Added ExcelModels + 3.29.0 - ExcelModels modifications for the use of OpenXml enhancment + 3.30.0 - Added MaxAmountOfFiles field to ModifyFileRequest + 3.30.1 - Added HtmlToPdfModels + 3.30.2 - Add AppendFile + 3.30.3 - Add ExtractIndexFileData + 3.30.4 - Edit IndexEvaluationRequest + 3.30.5 - Upgrade NuGets + 3.30.6 - Change ExcelDataRequest.FilePath to ExcelDataRequest.FileContent + 3.31.0 - Upgrade to dotnet8 + 4.0.0 - Updated project name + 4.1.1 - Added DeleteEmptyDirectoryRequest + 4.1.2 - Added ValidateFizeSizeInRangeRequest and FileSiseRequest + 4.1.3 - Update FileSourceRequest + 4.1.4 - Update ValidateFileRequest to add CheckFileCount Property and added new enum OperatorTypes + 4.1.5 - QueueRequest update to add ReturnResponse + + + + + + + + + + + + + + + diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/AppendFileRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/AppendFileRequest.cs new file mode 100644 index 0000000..9ee2873 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/AppendFileRequest.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.Text; +using LCU.Library.Internal.ApiResponse.Models; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class AppendFileRequest : ReturnResponse + { + [Required(ErrorMessage = "Destination path is required.")] + public string DestinationPath { get; set; } + [Required(ErrorMessage = "FileName is required.")] + public string FileName { get; set; } + [Required(ErrorMessage = "Extension is required.")] + public string Extension { get; set; } + [Required(ErrorMessage = "FileData is required.")] + public string FileData { get; set; } + + [NotMapped] + public string FullFilePath + { + get + { + return DestinationPath.EndsWith("\\") ? $"{DestinationPath}{FileNameWithExtension}" : $"{DestinationPath}\\{FileNameWithExtension}"; + } + } + + [NotMapped] + public string FileNameWithExtension + { + get + { + return Extension.StartsWith(".") ? $"{FileName}{Extension}" : $"{FileName}.{Extension}"; + } + } + + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/BatchParsePdfRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/BatchParsePdfRequest.cs new file mode 100644 index 0000000..b7eac40 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/BatchParsePdfRequest.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using LCU.Library.Internal.ApiResponse.Models; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class BatchParsePdfRequest : ReturnResponse + { + private string rootPath; + + [Required(AllowEmptyStrings = false)] + public string RootPath + { + get + { + return rootPath; + } + set + { + rootPath = string.IsNullOrWhiteSpace(value) || value.EndsWith("\\") ? value : $"{value}\\"; + } + } + + [Required(AllowEmptyStrings = false)] + public string PdfName { get; set; } + + public List BatchPdfs { get; set; } + + [NotMapped] + public string FullPdfPath + { + get + { + return RootPath + PdfName; + } + } + } + + public class BatchPdf + { + private string parsedPdfname; + + [Required] + [Range(1, int.MaxValue, ErrorMessage = "PageStart must be larger then 0.")] + public int PageStart { get; set; } + + [Required] + [Range(1, int.MaxValue, ErrorMessage = "PageStop must be larger then 0.")] + public int PageStop { get; set; } + + public string ParsedPdfname + { + get + { + return parsedPdfname; + } + set + { + parsedPdfname = value.ToLower().EndsWith(".pdf") || string.IsNullOrWhiteSpace(value) ? value : $"{value}.pdf"; + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/BulkOperationRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/BulkOperationRequest.cs new file mode 100644 index 0000000..fb984b5 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/BulkOperationRequest.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using LCU.Library.Internal.ApiResponse.Models; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class BulkOperationRequest : ReturnResponse + { + public List OperationRequests { get; set; } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/CopyFileRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/CopyFileRequest.cs new file mode 100644 index 0000000..42b728c --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/CopyFileRequest.cs @@ -0,0 +1,7 @@ +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class CopyFileRequest : FileRequest + { + + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/CreateDirectoryRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/CreateDirectoryRequest.cs new file mode 100644 index 0000000..18bd0bd --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/CreateDirectoryRequest.cs @@ -0,0 +1,38 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class CreateDirectoryRequest : ReturnResponse + { + [Required(ErrorMessage = "Directory name is required.")] + public string DirectoryName { get; set; } + + [Required(ErrorMessage = "Directory path is required")] + public string DirectoryPath { get; set; } + + public DynamicNameSetting NameSetting { get; set; } + + [NotMapped] + public string FinalDirectoryName + { + get + { + return DynamicNameBuilder.BuildDynamicName(DirectoryName, NameSetting); + } + } + + [NotMapped] + public string DirectoryDestination + { + get + { + var finalDestinationPath = DirectoryPath.EndsWith("\\") ? DirectoryPath : $"{DirectoryPath}\\"; + return $"{finalDestinationPath}{FinalDirectoryName}"; + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DecryptFileDataRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DecryptFileDataRequest.cs new file mode 100644 index 0000000..aa70132 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DecryptFileDataRequest.cs @@ -0,0 +1,72 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.IO; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class DecryptFileDataRequest : ReturnResponse + { + [Required(ErrorMessage = "Source path is required")] + public string SourcePath { get; set; } + + [Required(ErrorMessage = "Source file is required")] + public string SourceFile { get; set; } + + [Required(ErrorMessage = "Key source path is required")] + public string KeySourcePath { get; set; } + + [Required(ErrorMessage = "Key source file is required")] + public string KeySourceFile { get; set; } + + [Required(ErrorMessage = "PassPhrase is required")] + public string PassPhrase { get; set; } + + [Required(ErrorMessage = "Destination path is required.")] + public string DestinationPath { get; set; } + + public string DestinationFile { get; set; } + + public bool Overwrite { get; set; } + + public DynamicNameSetting FileNameSetting { get; set; } + + [NotMapped] public string FinalSourceFile => DynamicNameBuilder.BuildDynamicName(SourceFile, FileNameSetting); + + [NotMapped] + public string Source + { + get + { + var finalSourcePath = SourcePath.EndsWith("\\") ? SourcePath : $"{SourcePath}\\"; + finalSourcePath = Path.Combine(finalSourcePath, FinalSourceFile); + return finalSourcePath; + } + } + + [NotMapped] + public string Destination + { + get + { + var finalDestinationPath = DestinationPath.EndsWith("\\") ? DestinationPath : $"{DestinationPath}\\"; + var finalDestinationFileName = string.IsNullOrWhiteSpace(DestinationFile) ? SourceFile : DestinationFile; + finalDestinationPath = Path.Combine(finalDestinationPath, Path.GetFileNameWithoutExtension(finalDestinationFileName)); + return finalDestinationPath; + } + } + + [NotMapped] + public string KeySource + { + get + { + var finalKeyPath = KeySourcePath.EndsWith("\\") ? KeySourcePath : $"{KeySourcePath}\\"; + finalKeyPath = Path.Combine(finalKeyPath, KeySourceFile); + return finalKeyPath; + } + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteDirectoryRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteDirectoryRequest.cs new file mode 100644 index 0000000..f43dfcf --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteDirectoryRequest.cs @@ -0,0 +1,42 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class DeleteDirectoryRequest : ReturnResponse + { + [Required(ErrorMessage = "Directory name is required.")] + public string DirectoryName { get; set; } + + [Required(ErrorMessage = "Directory path is required")] + public string DirectoryRootPath { get; set; } + + [Range(0, 25, ErrorMessage = "MaxAmountOfDirectories must be between {1} and {2}.")] + public int MaxAmountOfDirectories { get; set; } = 0; + + public bool Recursive { get; set; } + + public DynamicNameSetting DirectoryNameSettings { get; set; } + + [NotMapped] + public string FinalDirectoryName + { + get + { + return DynamicNameBuilder.BuildDynamicName(DirectoryName, DirectoryNameSettings); + } + } + + [NotMapped] + public string FinalDirectoryPath + { + get + { + return $"{DirectoryRootPath}\\{FinalDirectoryName}"; + } + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteEmptyDirectoryRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteEmptyDirectoryRequest.cs new file mode 100644 index 0000000..621acf4 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteEmptyDirectoryRequest.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Library.Internal.ApiResponse.Models; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class DeleteEmptyDirectoryRequest : ReturnResponse + { + [Required(ErrorMessage = "Sub directory path is required.")] + public string SubDirectoryPath { get; set; } + + [Required(ErrorMessage = "Dynamic directory name is required")] + public string DynamicDirectoryName { get; set; } + + public DynamicNameSetting DirectoryNameSettings { get; set; } + + [NotMapped] + public string FinalDirectoryName + { + get + { + return DynamicNameBuilder.BuildDynamicName(DynamicDirectoryName, DirectoryNameSettings); + } + } + + [NotMapped] + public string FinalDirectoryPath + { + get + { + return SubDirectoryPath.EndsWith("\\") ? $"{SubDirectoryPath}{FinalDirectoryName}" : $"{SubDirectoryPath}\\{FinalDirectoryName}"; + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteFileRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteFileRequest.cs new file mode 100644 index 0000000..207623a --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DeleteFileRequest.cs @@ -0,0 +1,39 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class DeleteFileRequest : ReturnResponse + { + [Required(ErrorMessage = "Source path is required")] + public string SourcePath { get; set; } + + [Required(ErrorMessage = "Source file is required")] + public string SourceFile { get; set; } + + public DynamicNameSetting FileNameSetting { get; set; } + + public bool SucceedOnError { get; set; } + + [NotMapped] + public string FinalSourceFile + { + get + { + return DynamicNameBuilder.BuildDynamicName(SourceFile, FileNameSetting); + } + } + [NotMapped] + public string Source + { + get + { + var finalSourcePath = SourcePath.EndsWith("\\") ? SourcePath : $"{SourcePath}\\"; + return finalSourcePath + FinalSourceFile; + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DynamicDirectoryListRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DynamicDirectoryListRequest.cs new file mode 100644 index 0000000..6a6fac6 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DynamicDirectoryListRequest.cs @@ -0,0 +1,14 @@ +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using System.ComponentModel.DataAnnotations; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class DynamicDirectoryListRequest + { + [Required(ErrorMessage = "Path is required")] + public string Path { get; set; } + [Required(ErrorMessage = "Dynamic Directory Name is required")] + public string DynamicDirectoryName { get; set; } + public DynamicNameSetting DynamicFileSettings { get; set; } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DynamicFileListRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DynamicFileListRequest.cs new file mode 100644 index 0000000..cef6d4a --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/DynamicFileListRequest.cs @@ -0,0 +1,11 @@ +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class DynamicFileListRequest + { + public string Path { get; set; } + public string Filename { get; set; } + public DynamicNameSetting DynamicFileSettings { get; set; } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/EncryptFileDataRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/EncryptFileDataRequest.cs new file mode 100644 index 0000000..5a6149c --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/EncryptFileDataRequest.cs @@ -0,0 +1,66 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.IO; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class EncryptFileDataRequest : ReturnResponse + { + [Required(ErrorMessage = "Source path is required")] + public string SourcePath { get; set; } + + [Required(ErrorMessage = "Source file is required")] + public string SourceFile { get; set; } + + [Required(ErrorMessage = "Key source path is required")] + public string KeySourcePath { get; set; } + + [Required(ErrorMessage = "Key source file is required")] + public string KeySourceFile { get; set; } + + [Required(ErrorMessage = "Destination path is required.")] + public string DestinationPath { get; set; } + + public bool Overwrite { get; set; } + + public DynamicNameSetting FileNameSetting { get; set; } + + [NotMapped] public string FinalSourceFile => DynamicNameBuilder.BuildDynamicName(SourceFile, FileNameSetting); + + [NotMapped] + public string Source + { + get + { + var finalSourcePath = SourcePath.EndsWith("\\") ? SourcePath : $"{SourcePath}\\"; + finalSourcePath = Path.Combine(finalSourcePath, FinalSourceFile); + return finalSourcePath; + } + } + + [NotMapped] + public string Destination + { + get + { + var finalDestinationPath = DestinationPath.EndsWith("\\") ? DestinationPath : $"{DestinationPath}\\"; + finalDestinationPath = Path.Combine(finalDestinationPath, Path.GetFileNameWithoutExtension(FinalSourceFile)) + ".pgp"; + return finalDestinationPath; + } + } + + [NotMapped] + public string KeySource + { + get + { + var finalKeyPath = KeySourcePath.EndsWith("\\") ? KeySourcePath : $"{KeySourcePath}\\"; + finalKeyPath = Path.Combine(finalKeyPath, KeySourceFile); + return finalKeyPath; + } + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Requests/ExcelDataRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Requests/ExcelDataRequest.cs new file mode 100644 index 0000000..76f8f4d --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Requests/ExcelDataRequest.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +namespace LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Requests; + +public class ExcelDataRequest +{ + [Required(AllowEmptyStrings = false, ErrorMessage = "FileContent is required")] + public string FileContent { get; set; } + public List SheetRequests { get; set; } = null; + + public bool SkipSheetForExtraction(string sheetName, out SheetRequest currentSheetRequest) + { + currentSheetRequest = SheetRequests is null || !SheetRequests.Any() ? + null : + SheetRequests.FirstOrDefault(sheetReq => sheetReq.SheetName == sheetName); + + if (this is not null && + SheetRequests is not null && + SheetRequests.Any() && + currentSheetRequest is null) + { + return true; + } + + currentSheetRequest ??= new SheetRequest(); + return false; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Requests/SheetRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Requests/SheetRequest.cs new file mode 100644 index 0000000..a9f15bd --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Requests/SheetRequest.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +namespace LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Requests; + +public class SheetRequest +{ + [Required(AllowEmptyStrings = false, ErrorMessage = "Sheet Name is required")] + public string SheetName { get; set; } + public List Columns { get; set; } = null; + public List Rows { get; set; } = null; + + public bool SkipRowForExtraction(int rowIndex) + { + if (this is not null && Rows is not null && Rows.Any()) + { + if (!Rows.Contains(rowIndex)) + { + return true; + } + } + return false; + } + + public bool SkipColumnForExtraction(string columnName) + { + if (this is not null && Columns is not null && Columns.Any()) + { + if (!Columns.Contains(columnName, StringComparer.OrdinalIgnoreCase)) + { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/CellResponse.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/CellResponse.cs new file mode 100644 index 0000000..3881e6a --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/CellResponse.cs @@ -0,0 +1,9 @@ +namespace LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Responses; + +public class CellResponse +{ + public string ColumnName { get; set; } + public int Row { get; set; } + public string Value { get; set; } + public string Formula { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/ExcelDataResponse.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/ExcelDataResponse.cs new file mode 100644 index 0000000..d9543f6 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/ExcelDataResponse.cs @@ -0,0 +1,11 @@ +namespace LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Responses; + +public class ExcelDataResponse +{ + public ExcelDataResponse() + { + WorkBook = new WorkBook(); + } + + public WorkBook WorkBook { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/SheetResponse.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/SheetResponse.cs new file mode 100644 index 0000000..899f307 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/SheetResponse.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +namespace LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Responses; + +public class SheetResponse +{ + public SheetResponse(string sheetName) + { + SheetName = sheetName; + CellResponses = new List(); + } + + public string SheetName { get; set; } + public List CellResponses { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/WorkBook.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/WorkBook.cs new file mode 100644 index 0000000..cc3263e --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExcelModels/Responses/WorkBook.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +namespace LCU.Library.Internal.FileOperationsModels.Models.ExcelModels.Responses; + +public class WorkBook +{ + public WorkBook() + { + SheetResponses = new List(); + } + + public List SheetResponses { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExtractIndexFileDataRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExtractIndexFileDataRequest.cs new file mode 100644 index 0000000..b11b243 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ExtractIndexFileDataRequest.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.IO; +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Enums; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using LCU.Library.Internal.LcuEvaluationModels.Entities; + + +namespace LCU.Library.Internal.FileOperationsModels.Models; + +public class ExtractIndexFileDataRequest : ReturnResponse +{ + public string IndexFilePath { get; set; } + public DynamicNameSetting IndexFilePathSetting { get; set; } + public string IndexFileName { get; set; } + public DynamicNameSetting IndexFileNameSetting { get; set; } + public Delimiters DelimiterType { get; set; } + public bool ExtractAllIndexes { get; set; } + public List IndexesToExtract { get; set; } + public EvaluationDetail EvaluationRule { get; set; } + public string DestinationIndexFileName { get; set; } + public string DestinationFilePath { get; set; } + public DynamicNameSetting DestinationFilePathSetting { get; set; } + public bool AppendToDestinationIndexFile { get; set; } + public bool IndexHasHeaders { get; set; } + + [NotMapped] + public string FinalIndexFileName => DynamicNameBuilder.BuildDynamicName(IndexFileName, IndexFileNameSetting); + + [NotMapped] + public string FinalIndexFileDirectory + { + get + { + var indexFilePath = IndexFilePath.EndsWith("\\") ? IndexFilePath : $"{IndexFilePath}\\"; + return DynamicNameBuilder.BuildDynamicName(indexFilePath, IndexFilePathSetting); + } + } + + [NotMapped] + public string FinalIndexFilePath => Path.Combine(FinalIndexFileDirectory, FinalIndexFileName); + + [NotMapped] + public string FinalDestinationPath + { + get + { + var destinationFilePath = DestinationFilePath.EndsWith("\\") ? DestinationFilePath : $"{DestinationFilePath}\\"; + return DynamicNameBuilder.BuildDynamicName(destinationFilePath, DestinationFilePathSetting); + } + } + + [NotMapped] + public string FinalDestinationIndexFilePath => Path.Combine(FinalDestinationPath, DestinationIndexFileName); +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/FileOperationType.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/FileOperationType.cs new file mode 100644 index 0000000..7a4e8c2 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/FileOperationType.cs @@ -0,0 +1,25 @@ +namespace LCU.Library.Internal.FileOperationsModels.Models; +public enum FileOperationType +{ + Move = 0, + Copy = 1, + Delete = 2, + Rename = 3, + ZipDirectory = 4, + CreateDirectory = 5, + RenameDirectory = 6, + BulkMoveFile = 7, + UnzipFile = 8, + EncryptFileData = 9, + DecryptFileData = 10, + Modify = 11, + UnzipPasswordProtectedFile = 12, + BatchPdfParser = 13, + WriteFile = 14, + DeleteDirectory = 15, + Validate = 16, + AppendFile = 17, + ExtractIndexFileData = 18, + DeleteEmptyDirectory = 19, + ValidateFileSizeInRange = 20 +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/FileRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/FileRequest.cs new file mode 100644 index 0000000..917fb0b --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/FileRequest.cs @@ -0,0 +1,66 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public abstract class FileRequest : ReturnResponse + { + [Required(ErrorMessage = "Source path is required")] + public string SourcePath { get; set; } + + [Required(ErrorMessage = "Source file is required")] + public string SourceFile { get; set; } + + [Required(ErrorMessage = "Destination path is required.")] + public string DestinationPath { get; set; } + + public string DestinationName { get; set; } + + public bool Overwrite { get; set; } + + public bool SucceedOnError { get; set; } + + public DynamicNameSetting FileNameSetting { get; set; } + + [NotMapped] + public string FinalSourceFile + { + get + { + return DynamicNameBuilder.BuildDynamicName(SourceFile, FileNameSetting); + } + } + + [NotMapped] + public string FinalDestinationName + { + get + { + return string.IsNullOrWhiteSpace(DestinationName) ? FinalSourceFile : DestinationName; + } + } + + [NotMapped] + public string Source + { + get + { + var finalSourcePath = SourcePath.EndsWith("\\") ? SourcePath : $"{SourcePath}\\"; + return finalSourcePath + FinalSourceFile; + } + } + + [NotMapped] + public string Destination + { + get + { + var finalDestinationPath = DestinationPath.EndsWith("\\") ? DestinationPath : $"{DestinationPath}\\"; + return finalDestinationPath + FinalDestinationName; + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/HtmlToPdfRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/HtmlToPdfRequest.cs new file mode 100644 index 0000000..3949642 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/HtmlToPdfRequest.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace LCU.Library.Internal.FileOperationsModels.Models.HtmlToPdfModels; + +public class HtmlToPdfRequest +{ + [Required(AllowEmptyStrings = false)] + public string HtmlTemplateFilePath { get; set; } + [Required] + [RegularExpression(@"^[^\.]+$", ErrorMessage = "File extensions are not allowed.")] + public string PdfName { get; set; } + public bool KeepHtmlFile { get; set; } = false; + public bool KeepPdfFile { get; set; } = false; + [Required(AllowEmptyStrings = false)] + public string HtmlSaveFilePath { get; set; } + [Required(AllowEmptyStrings = false)] + public string PdfSaveFilePath { get; set; } + public List BodyTagValues { get; set; } + public List BodyValueCollections { get; set; } + + [NotMapped] + public string FinalHtmlSaveFilePath + { + get + { + var finalHtmlSavePath = HtmlSaveFilePath.EndsWith("\\") ? + HtmlSaveFilePath : + $"{HtmlSaveFilePath}\\"; + return finalHtmlSavePath; + } + } + + [NotMapped] + public string FinalPdfSaveFilePath + { + get + { + var finalPdfSaveFilePath = PdfSaveFilePath.EndsWith("\\") ? + PdfSaveFilePath : + $"{PdfSaveFilePath}\\"; + return finalPdfSaveFilePath; + } + } + + [NotMapped] + public string FinalHtmlSaveFilePathWithFile + { + get + { + return $"{FinalHtmlSaveFilePath}{PdfName}.html"; + } + } + + [NotMapped] + public string FinalPdfSaveFilePathWithFile + { + get + { + return $"{FinalPdfSaveFilePath}{PdfName}.pdf"; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/TagValue.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/TagValue.cs new file mode 100644 index 0000000..70cf5a1 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/TagValue.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; +namespace LCU.Library.Internal.FileOperationsModels.Models.HtmlToPdfModels; + +public class TagValue +{ + [Required(AllowEmptyStrings = false)] + public string Tag { get; set; } + [Required(AllowEmptyStrings = false)] + public string Value { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/ValueCollection.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/ValueCollection.cs new file mode 100644 index 0000000..c3a4db4 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/ValueCollection.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +namespace LCU.Library.Internal.FileOperationsModels.Models.HtmlToPdfModels; + +public class ValueCollection +{ + [Required(AllowEmptyStrings = false)] + public string CollectionTag { get; set; } + [Required(AllowEmptyStrings = false)] + public string CollectionHtmlTemplateFilePath { get; set; } + public List ValueCollectionItems { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/ValueCollectionItem.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/ValueCollectionItem.cs new file mode 100644 index 0000000..0f7dc11 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/HtmlToPdfModels/ValueCollectionItem.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; +namespace LCU.Library.Internal.FileOperationsModels.Models.HtmlToPdfModels; + +public class ValueCollectionItem +{ + public List TagValues { get; set; } + public List ValueCollections { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ModifyFileRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ModifyFileRequest.cs new file mode 100644 index 0000000..f221b57 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ModifyFileRequest.cs @@ -0,0 +1,78 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class ModifyFileRequest : ReturnResponse + { + [Required(ErrorMessage = "Source path is required")] + public string SourcePath { get; set; } + [Required(ErrorMessage = "Source file is required")] + public string SourceFile { get; set; } + public string PrependText { get; set; } + public string SourceExtension { get; set; } + public string AppendText { get; set; } + public string NewExtension { get; set; } + public int MaxAmountOfFiles { get; set; } = 0; + public bool SucceedOnError { get; set; } + public DynamicNameSetting FileNameSetting { get; set; } + [NotMapped] + public string OriginalSource + { + get + { + var finalSourcePath = SourcePath.EndsWith("\\") ? SourcePath : $"{SourcePath}\\"; + return finalSourcePath + OriginalSourceFile; + } + } + [NotMapped] + public string OriginalSourceFile + { + get + { + var fileName = DynamicNameBuilder.BuildDynamicName($"{SourceFile}.{SourceExtension}", FileNameSetting); + return fileName; + } + } + [NotMapped] + public string FinalSourceFile + { + get + { + var fileName = DynamicNameBuilder.BuildDynamicName(SourceFile, FileNameSetting); + var finalFileName = $"{PrependText}{fileName}{AppendText}.{FinalExtension}"; + return finalFileName; + } + } + [NotMapped] + public string FinalSource + { + get + { + var finalSourcePath = SourcePath.EndsWith("\\") ? SourcePath : $"{SourcePath}\\"; + return finalSourcePath + FinalSourceFile; + } + } + public static string GetNameWithoutExtension(string name, string extension) + { + var fileNameWithOutExtension = name.Replace("." + extension, string.Empty); + return fileNameWithOutExtension; + } + [NotMapped] + public string FinalExtension + { + get + { + if (string.IsNullOrWhiteSpace(NewExtension)) + { + return SourceExtension; + } + + return NewExtension; + } + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/MoveFileRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/MoveFileRequest.cs new file mode 100644 index 0000000..80fed1f --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/MoveFileRequest.cs @@ -0,0 +1,6 @@ +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class MoveFileRequest : FileRequest + { + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/DateFormatSetting.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/DateFormatSetting.cs new file mode 100644 index 0000000..eff8fab --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/DateFormatSetting.cs @@ -0,0 +1,8 @@ +namespace LCU.Library.Internal.FileOperationsModels.Models.NameSettings +{ + public class DateFormatSetting + { + public string Tag { get; set; } + public string DateFormat { get; set; } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/DynamicNameSetting.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/DynamicNameSetting.cs new file mode 100644 index 0000000..da0b48c --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/DynamicNameSetting.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace LCU.Library.Internal.FileOperationsModels.Models.NameSettings +{ + public class DynamicNameSetting + { + public List DateFormatSettings { get; set; } + public List RegexSettings { get; set; } + public int DaysFromToday { get; set; } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/RegexSetting.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/RegexSetting.cs new file mode 100644 index 0000000..986c398 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/NameSettings/RegexSetting.cs @@ -0,0 +1,8 @@ +namespace LCU.Library.Internal.FileOperationsModels.Models.NameSettings +{ + public class RegexSetting + { + public string Tag { get; set; } + public string Regex { get; set; } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ParsePdfRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ParsePdfRequest.cs new file mode 100644 index 0000000..ff87ad3 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ParsePdfRequest.cs @@ -0,0 +1,46 @@ +using System.ComponentModel.DataAnnotations; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class ParsePdfRequest + { + private string rootPath; + private string parsedPdfname; + + [Required(AllowEmptyStrings = false)] + public string RootPath + { + get + { + return rootPath; + } + set + { + rootPath = string.IsNullOrWhiteSpace(value) || value.EndsWith("\\") ? value : $"{value}\\"; + } + } + + [Required(AllowEmptyStrings = false)] + public string PdfName { get; set; } + + [Required] + [Range(1, int.MaxValue, ErrorMessage = "PageStart must be larger then 0.")] + public int PageStart { get; set; } + + [Required] + [Range(1, int.MaxValue, ErrorMessage = "PageStop must be larger then 0.")] + public int PageStop { get; set; } + + public string ParsedPdfname + { + get + { + return parsedPdfname; + } + set + { + parsedPdfname = value.ToLower().EndsWith(".pdf") || string.IsNullOrWhiteSpace(value) ? value : $"{value}.pdf"; + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/QueueRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/QueueRequest.cs new file mode 100644 index 0000000..913ddc2 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/QueueRequest.cs @@ -0,0 +1,11 @@ +using System; +using LCU.Library.Internal.ApiResponse.Models; +namespace LCU.Library.Internal.FileOperationsModels.Models; +public class QueueRequest +{ + public ReturnResponse ReturnResponse { get; set; } + public FileOperationType FileOperationType { get; set; } + public string Request { get; set; } + public int? Attempts { get; set; } + public DateTime QueueTimeStamp { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RenameDirectoryRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RenameDirectoryRequest.cs new file mode 100644 index 0000000..b00b815 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RenameDirectoryRequest.cs @@ -0,0 +1,62 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class RenameDirectoryRequest : ReturnResponse + { + [Required(ErrorMessage = "Source directory path is required.")] + public string SourceDirectoryPath { get; set; } + + [Required(ErrorMessage = "Source directory name is required.")] + public string SourceDirectoryName { get; set; } + + [Required(ErrorMessage = "Directory name is required.")] + public string NewDirectoryName { get; set; } + + public DynamicNameSetting SourceDirectoryNameSetting { get; set; } + + public DynamicNameSetting NewDirectoryNameSetting { get; set; } + + [NotMapped] + public string FinalSourceDirectoryName + { + get + { + return DynamicNameBuilder.BuildDynamicName(SourceDirectoryName, SourceDirectoryNameSetting); + } + } + + [NotMapped] + public string FinalSourceDirectoryPath + { + get + { + var finalDestinationPath = SourceDirectoryPath.EndsWith("\\") ? SourceDirectoryPath : $"{SourceDirectoryPath}\\"; + return $"{finalDestinationPath}{FinalSourceDirectoryName}"; + } + } + + [NotMapped] + public string FinalNewDirectoryName + { + get + { + return DynamicNameBuilder.BuildDynamicName(NewDirectoryName, NewDirectoryNameSetting); + } + } + + [NotMapped] + public string NewDirectoryDestination + { + get + { + var finalDestinationPath = SourceDirectoryPath.EndsWith("\\") ? SourceDirectoryPath : $"{SourceDirectoryPath}\\"; + return $"{finalDestinationPath}{FinalNewDirectoryName}"; + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RenameFileRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RenameFileRequest.cs new file mode 100644 index 0000000..91b4346 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RenameFileRequest.cs @@ -0,0 +1,62 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class RenameFileRequest : ReturnResponse + { + [Required(ErrorMessage = "Source path is required.")] + public string SourcePath { get; set; } + + [Required(ErrorMessage = "Source file is required.")] + public string SourceFile { get; set; } + + [Required(ErrorMessage = "New file name is required.")] + public string NewFilename { get; set; } + + public DynamicNameSetting SourceNameSetting { get; set; } + public DynamicNameSetting NewNameSetting { get; set; } + public bool SucceedOnError { get; set; } + + [NotMapped] + public string FinalSourceFile + { + get + { + return DynamicNameBuilder.BuildDynamicName(SourceFile, SourceNameSetting); + } + } + + [NotMapped] + public string Source + { + get + { + var finalSourcePath = SourcePath.EndsWith("\\") ? SourcePath : $"{SourcePath}\\"; + return finalSourcePath + FinalSourceFile; + } + } + + [NotMapped] + public string FinalNewFileName + { + get + { + return DynamicNameBuilder.BuildDynamicName(NewFilename, NewNameSetting); + } + } + + [NotMapped] + public string NewFilenamePath + { + get + { + var finalSourcePath = SourcePath.EndsWith("\\") ? SourcePath : $"{SourcePath}\\"; + return finalSourcePath + FinalNewFileName; + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RequestResponse.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RequestResponse.cs new file mode 100644 index 0000000..e09d7d5 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/RequestResponse.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using LCU.Library.Internal.ApiResponse.Models; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class RequestResponse + { + public RequestResponse() + { + MessageStack = new List(); + IsSuccessful = true; + ReturnResponse = null; + } + + public List MessageStack { get; set; } + public bool IsSuccessful { get; set; } + public ReturnResponse ReturnResponse { get; set; } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileDestinationRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileDestinationRequest.cs new file mode 100644 index 0000000..8881897 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileDestinationRequest.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.IO; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; + +namespace LCU.Library.Internal.FileOperationsModels.Models.Requests; + +public class FileDestinationRequest +{ + [Required(ErrorMessage = "Destination path is required.")] + public string DestinationPath { get; set; } + public DynamicNameSetting DestinationPathSettings { get; set; } + public string DestinationName { get; set; } = string.Empty; + public bool Overwrite { get; set; } + + [NotMapped] + public string FinalDestinationPath + { + get + { + var destinationPath = DestinationPath.EndsWith("\\") ? DestinationPath : $"{DestinationPath}\\"; + return DynamicNameBuilder.BuildDynamicName(destinationPath, DestinationPathSettings); + } + } + + [NotMapped] + public string Destination => Path.Combine(FinalDestinationPath, DestinationName); +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileSizeRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileSizeRequest.cs new file mode 100644 index 0000000..45dbde0 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileSizeRequest.cs @@ -0,0 +1,9 @@ +using LCU.Library.Internal.FileOperationsModels.Enums; + +namespace LCU.Library.Internal.FileOperationsModels.Models.Requests; + +public class FileSizeRequest : IFileSourceRequest +{ + public FileSourceRequest FileSourceRequest { get; set; } = new(); + public FileSizeTypes FileSizeType { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileSourceRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileSourceRequest.cs new file mode 100644 index 0000000..62a8c08 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/FileSourceRequest.cs @@ -0,0 +1,35 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.IO; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; + +namespace LCU.Library.Internal.FileOperationsModels.Models.Requests; + +public class FileSourceRequest +{ + [Required(ErrorMessage = "Source path is required")] + public string SourcePath { get; set; } = string.Empty; + public DynamicNameSetting SourcePathSettings { get; set; } = new(); + + [Required(ErrorMessage = "Source file is required")] + public string SourceFile { get; set; } = string.Empty; + + public DynamicNameSetting SourceFileSettings { get; set; } = new(); + + [NotMapped] + public string FinalSourceFile => DynamicNameBuilder.BuildDynamicName(SourceFile, SourceFileSettings); + + [NotMapped] + public string FinalSourcePath + { + get + { + var sourcePath = SourcePath.EndsWith("\\") ? SourcePath : $"{SourcePath}\\"; + return DynamicNameBuilder.BuildDynamicName(sourcePath, SourcePathSettings); + } + } + + [NotMapped] + public string Source => Path.Combine(FinalSourcePath, FinalSourceFile); +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/IFileDestinationRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/IFileDestinationRequest.cs new file mode 100644 index 0000000..94ab6b2 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/IFileDestinationRequest.cs @@ -0,0 +1,6 @@ +namespace LCU.Library.Internal.FileOperationsModels.Models.Requests; + +public interface IFileDestinationRequest +{ + public FileDestinationRequest FileDestinationRequest { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/IFileSourceRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/IFileSourceRequest.cs new file mode 100644 index 0000000..9dddb2f --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/IFileSourceRequest.cs @@ -0,0 +1,6 @@ +namespace LCU.Library.Internal.FileOperationsModels.Models.Requests; + +public interface IFileSourceRequest +{ + public FileSourceRequest FileSourceRequest { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/ValidateFileRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/ValidateFileRequest.cs new file mode 100644 index 0000000..73cd3ae --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/ValidateFileRequest.cs @@ -0,0 +1,14 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Enums; +using System.ComponentModel; +namespace LCU.Library.Internal.FileOperationsModels.Models.Requests; + +public class ValidateFileRequest : ReturnResponse, IFileSourceRequest +{ + public FileSourceRequest FileSourceRequest { get; set; } = new(); + [DefaultValue(false)] + public bool CheckFileCount { get; set; } = false; + public int FileCount { get; set; } + public OperatorTypes OperatorType { get; set; } + public bool FileDoesExist { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/ValidateFileSizeInRangeRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/ValidateFileSizeInRangeRequest.cs new file mode 100644 index 0000000..72bf3fb --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Requests/ValidateFileSizeInRangeRequest.cs @@ -0,0 +1,12 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Enums; + +namespace LCU.Library.Internal.FileOperationsModels.Models.Requests; + +public class ValidateFileSizeInRangeRequest : ReturnResponse, IFileSourceRequest +{ + public FileSourceRequest FileSourceRequest { get; set; } = new(); + public FileSizeTypes FileSizeType { get; set; } + public decimal StartFileSizeRange { get; set; } + public decimal EndFileSizeRange { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Responses/FileSizeResponse.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Responses/FileSizeResponse.cs new file mode 100644 index 0000000..10e98b3 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/Responses/FileSizeResponse.cs @@ -0,0 +1,26 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Reflection; +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Enums; + +namespace LCU.Library.Internal.FileOperationsModels.Models.Responses; + +public class FileSizeResponse : ReturnResponse +{ + public string FileName { get; set; } = string.Empty; + public decimal FileSize { get; set; } + public FileSizeTypes FileSizeType { get; set; } + + [NotMapped] + public string FileSizeString + { + get + { + var fileSizeType = FileSizeType.GetType().GetMember(FileSizeType.ToString()).First().GetCustomAttribute()?.Name; + return $"{Math.Round(FileSize, 6):0.######} {fileSizeType}"; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/UnzipFileRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/UnzipFileRequest.cs new file mode 100644 index 0000000..99563d0 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/UnzipFileRequest.cs @@ -0,0 +1,96 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class UnzipFileRequest : ReturnResponse + { + [Required(ErrorMessage = "Source path is required")] + public string SourceDirectory { get; set; } + + [Required(ErrorMessage = "Zip file name is required.")] + public string ZipFileName { get; set; } + + [Required(ErrorMessage = "Destination path is required.")] + public string DestinationPath { get; set; } + + public bool UseZipFileNameAsFinalDestination { get; set; } + public bool IgnoreFileNameSubFolderAsDestination { get; set; } + + public int MaxFileDepth { get; set; } = 0; + + public DynamicNameSetting FileNameSetting { get; set; } + public DynamicNameSetting SourceDirectorySetting { get; set; } + public DynamicNameSetting DestinationPathSetting { get; set; } + + [NotMapped] + public string ZipFileNameWithDate + { + get + { + return DynamicNameBuilder.BuildDynamicName(ZipFileName, FileNameSetting); + } + } + + [NotMapped] + public string FormattedSourceDirectoryPath + { + get + { + var sourceDirectoryWithDates = DynamicNameBuilder.BuildDynamicName(SourceDirectory, SourceDirectorySetting); + return sourceDirectoryWithDates.EndsWith("\\") ? sourceDirectoryWithDates : $"{sourceDirectoryWithDates}\\"; + } + } + + [NotMapped] + public string FinalZipFileName + { + get + { + return ZipFileNameWithDate.ToUpper().EndsWith(".ZIP") ? ZipFileNameWithDate : $"{ZipFileNameWithDate}.zip"; + } + } + + [NotMapped] + public string SourceFilePath + { + get + { + return FormattedSourceDirectoryPath + FinalZipFileName; + } + } + + [NotMapped] + public string FormattedDestinationPath + { + get + { + var destinationPathDates = DynamicNameBuilder.BuildDynamicName(DestinationPath, DestinationPathSetting); + return destinationPathDates.EndsWith("\\") ? destinationPathDates : $"{destinationPathDates}\\"; + } + } + + [NotMapped] + public string ZipFileNameWithoutZip + { + get + { + return FinalZipFileName.ToUpper().EndsWith(".ZIP") ? + FinalZipFileName.ToUpper().Replace(".ZIP", "") : + FinalZipFileName; + } + } + + [NotMapped] + public string DestinationPathZipFileNameWithoutZip + { + get + { + return $"{FormattedDestinationPath}{ZipFileNameWithoutZip}\\"; + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/UnzipPasswordProtectedFileRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/UnzipPasswordProtectedFileRequest.cs new file mode 100644 index 0000000..9767099 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/UnzipPasswordProtectedFileRequest.cs @@ -0,0 +1,76 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class UnzipPasswordProtectedFileRequest : ReturnResponse + { + [Required(ErrorMessage = "Source path is required")] + public string SourceDirectory { get; set; } + + [Required(ErrorMessage = "Password protected zip file name is required")] + public string PasswordProtectedZipFileName { get; set; } + + [Required(ErrorMessage = "Password is required")] + public string Password { get; set; } + + [Required(ErrorMessage = "Destination path is required")] + public string DestinationPath { get; set; } + + public bool UseZipFileNameAsFinalDestination { get; set; } + + public DynamicNameSetting FileNameSetting { get; set; } + public DynamicNameSetting SourceDirectorySetting { get; set; } + public DynamicNameSetting DestinationPathSetting { get; set; } + + [NotMapped] + public string PasswordProtectedZipFileNameWithDate + { + get + { + return DynamicNameBuilder.BuildDynamicName(PasswordProtectedZipFileName, FileNameSetting); + } + } + + [NotMapped] + public string FormattedSourceDirectoryPath + { + get + { + var sourceDirectoryWithDates = DynamicNameBuilder.BuildDynamicName(SourceDirectory, SourceDirectorySetting); + return sourceDirectoryWithDates.EndsWith("\\") ? sourceDirectoryWithDates : $"{sourceDirectoryWithDates}\\"; + } + } + + [NotMapped] + public string FinalPasswordProtectedZipFileName + { + get + { + return PasswordProtectedZipFileNameWithDate.ToUpper().EndsWith(".ZIP") ? PasswordProtectedZipFileNameWithDate : $"{PasswordProtectedZipFileNameWithDate}.zip"; + } + } + + [NotMapped] + public string SourceFilePath + { + get + { + return FormattedSourceDirectoryPath + FinalPasswordProtectedZipFileName; + } + } + + [NotMapped] + public string FormattedDestinationPath + { + get + { + var destinationPath = DynamicNameBuilder.BuildDynamicName(DestinationPath, DestinationPathSetting); + return destinationPath.EndsWith("\\") ? destinationPath : $"{destinationPath}\\"; ; + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/WriteFileRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/WriteFileRequest.cs new file mode 100644 index 0000000..89838e4 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/WriteFileRequest.cs @@ -0,0 +1,38 @@ +using LCU.Library.Internal.ApiResponse.Models; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class WriteFileRequest : ReturnResponse + { + [Required(ErrorMessage = "Destination path is required.")] + public string DestinationPath { get; set; } + [Required(ErrorMessage = "FileName is required.")] + public string FileName { get; set; } + [Required(ErrorMessage = "Extension is required.")] + public string Extension { get; set; } + [Required(ErrorMessage = "FileData is required.")] + public string FileData { get; set; } + public bool Overwrite { get; set; } = false; + + [NotMapped] + public string FullFilePath + { + get + { + return DestinationPath.EndsWith("\\") ? $"{DestinationPath}{FileNameWithExtension}" : $"{DestinationPath}\\{FileNameWithExtension}"; + } + } + + [NotMapped] + public string FileNameWithExtension + { + get + { + return Extension.StartsWith(".") ? $"{FileName}{Extension}" : $"{FileName}.{Extension}"; + } + } + + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ZipDirectoryRequest.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ZipDirectoryRequest.cs new file mode 100644 index 0000000..938d4a4 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Models/ZipDirectoryRequest.cs @@ -0,0 +1,53 @@ +using LCU.Library.Internal.ApiResponse.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using LCU.Library.Internal.FileOperationsModels.Shared; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.Library.Internal.FileOperationsModels.Models +{ + public class ZipDirectoryRequest : ReturnResponse + { + [Required(ErrorMessage = "Source path is required")] + public string SourceDirectory { get; set; } + + public string DirectoryName { get; set; } + + [Required(ErrorMessage = "Zip path is required.")] + public string ZipPath { get; set; } + + public string ZipFileName { get; set; } + + public DynamicNameSetting FileNameSetting { get; set; } + + [NotMapped] + public string DynamicDirectoryName + { + get + { + return DynamicNameBuilder.BuildDynamicName(DirectoryName, FileNameSetting); + } + } + + [NotMapped] + public string FormattedSourceDirectory + { + get + { + return SourceDirectory.EndsWith("\\") ? SourceDirectory : $"{SourceDirectory}\\"; + } + } + + [NotMapped] + public string ZipDestination + { + get + { + var finalDestinationPath = ZipPath.EndsWith("\\") ? ZipPath : $"{ZipPath}\\"; + var finalZipName = !string.IsNullOrWhiteSpace(ZipFileName) ? ZipFileName : DirectoryName; + finalZipName = finalZipName.ToUpper().EndsWith(".ZIP") ? finalZipName : $"{finalZipName}.zip"; + return finalDestinationPath + finalZipName; + } + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/Clone.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/Clone.cs new file mode 100644 index 0000000..7b768de --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/Clone.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace LCU.Library.Internal.FileOperationsModels.Shared +{ + public static class Clone + { + public static T DeepClone(this T source) + { + if (ReferenceEquals(source, null)) + { + return default; + } + + var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace }; + return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source), deserializeSettings); + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/DynamicNameBuilder.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/DynamicNameBuilder.cs new file mode 100644 index 0000000..b486939 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/DynamicNameBuilder.cs @@ -0,0 +1,130 @@ +using LCU.Library.Internal.FileOperationsModels.Models; +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using System; +using System.Collections.Generic; +using LCU.Library.Internal.FileOperationsModels.Models.Requests; + +namespace LCU.Library.Internal.FileOperationsModels.Shared +{ + public static class DynamicNameBuilder + { + public static List BuildListOfRequest(TRequest originalRequest) where TRequest : FileRequest + { + var nameList = RegexNameHandler.BuildFileList(originalRequest.SourcePath, + originalRequest.FinalSourceFile, + originalRequest.FileNameSetting); + var requests = new List(); + + foreach (var name in nameList) + { + var newRequest = originalRequest.DeepClone(); + newRequest.SourceFile = name; + + requests.Add(newRequest); + } + + return requests; + } + + public static List BuildListOfRequests(TRequest originalRequest) where TRequest : IFileSourceRequest + { + var nameList = RegexNameHandler.BuildFileList(originalRequest.FileSourceRequest.FinalSourcePath, + originalRequest.FileSourceRequest.FinalSourceFile, + originalRequest.FileSourceRequest.SourceFileSettings); + + var requests = new List(); + + foreach (var name in nameList) + { + var newRequest = originalRequest.DeepClone(); + newRequest.FileSourceRequest.SourceFile = name; + + requests.Add(newRequest); + } + + return requests; + } + + public static List BuildListOfRequest(ModifyFileRequest originalRequest) + { + var nameList = RegexNameHandler.BuildFileList(originalRequest.SourcePath, + originalRequest.OriginalSourceFile, + originalRequest.FileNameSetting); + + var requests = new List(); + + foreach (var name in nameList) + { + var newRequest = originalRequest.DeepClone(); + newRequest.SourceFile = ModifyFileRequest.GetNameWithoutExtension(name, newRequest.SourceExtension); + + requests.Add(newRequest); + } + + return requests; + } + + [Obsolete ("As of 7/7/2025, this method is obsolete. Use BuildListOfRequests instead.")] + public static List BuildListOfRequest(ExtractIndexFileDataRequest originalRequest) + { + var nameList = RegexNameHandler.BuildFileList(originalRequest.FinalIndexFileDirectory, + originalRequest.FinalIndexFileName, + originalRequest.IndexFileNameSetting); + + var requests = new List(); + + foreach (var name in nameList) + { + var newRequest = originalRequest.DeepClone(); + newRequest.IndexFileName = name; + + requests.Add(newRequest); + } + + return requests; + } + + public static string BuildDynamicName(string name, DynamicNameSetting nameSetting) + { + if (nameSetting == null || + nameSetting.DateFormatSettings == null) + { + return name; + } + + name = AddFormattedDateSettings(name, nameSetting); + return name; + } + + public static string AddFormattedDateSettings(string name, DynamicNameSetting nameSettings) + { + foreach (var formatSetting in nameSettings.DateFormatSettings) + { + var convertedFormat = DateTimeStringBuilder(formatSetting.DateFormat, nameSettings.DaysFromToday); + name = ReplaceTagWithFormattedSTring(name, formatSetting.Tag, convertedFormat); + } + + return name; + } + + public static string DateTimeStringBuilder(string format, int daysFromToday) + { + if (string.IsNullOrWhiteSpace(format)) + { + return ""; + } + + return DateTime.Now.AddDays(daysFromToday).ToString(format); + } + + public static string ReplaceTagWithFormattedSTring(string name, string tag, string convertedFormat) + { + if (string.IsNullOrEmpty(tag) || string.IsNullOrWhiteSpace(convertedFormat)) + { + return name.Trim(); + } + + return name.Replace(tag, convertedFormat); + } + } +} diff --git a/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/RegexNameHandler.cs b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/RegexNameHandler.cs new file mode 100644 index 0000000..c005707 --- /dev/null +++ b/LCU.API.Internal.FileOperations/LCU.Library.Internal.FileOperationsModels/Shared/RegexNameHandler.cs @@ -0,0 +1,69 @@ +using LCU.Library.Internal.FileOperationsModels.Models.NameSettings; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; + +namespace LCU.Library.Internal.FileOperationsModels.Shared +{ + public static class RegexNameHandler + { + public static ICollection BuildFileList(string path, string name, DynamicNameSetting nameSetting) + { + var fileList = new List(); + + if (nameSetting == null || + nameSetting.RegexSettings == null || + !nameSetting.RegexSettings.Any()) + { + fileList.Add(name); + return fileList; + } + + DirectoryInfo localDirectory = new DirectoryInfo(path); + var regexName = BuildRegexName(name, nameSetting); + fileList = localDirectory.GetFiles().Where(file => regexName.IsMatch(file.Name)).Select(file => file.Name).ToList(); + + return fileList; + } + + public static ICollection BuildDirectoryList(string path, string name, DynamicNameSetting nameSetting) + { + var directoriesList = new List(); + + if (nameSetting == null || + nameSetting.RegexSettings == null || + string.IsNullOrWhiteSpace(name)) + { + directoriesList.Add(name); + return directoriesList; + } + + DirectoryInfo localDirectory = new DirectoryInfo(path); + var regexName = BuildRegexName(name, nameSetting); + directoriesList = localDirectory.GetDirectories().Where(directory => regexName.IsMatch(directory.Name)).Select(directory => directory.Name).ToList(); + + return directoriesList; + } + + public static Regex BuildRegexName(string name, DynamicNameSetting nameSettings) + { + foreach (var formatSetting in nameSettings.RegexSettings) + { + name = ReplaceTagWithRegex(name, formatSetting.Tag, formatSetting.Regex); + } + + return new Regex(name); + } + + public static string ReplaceTagWithRegex(string name, string tag, string regex) + { + if (string.IsNullOrEmpty(tag) || string.IsNullOrWhiteSpace(regex)) + { + return name.Trim(); + } + + return name.Replace(tag, regex); + } + } +} diff --git a/LCU.API.Internal.FileOperations/README.md b/LCU.API.Internal.FileOperations/README.md new file mode 100644 index 0000000..e37e4b1 --- /dev/null +++ b/LCU.API.Internal.FileOperations/README.md @@ -0,0 +1,20 @@ +# Introduction +TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project. + +# Getting Started +TODO: Guide users through getting your code up and running on their own system. In this section you can talk about: +1. Installation process +2. Software dependencies +3. Latest releases +4. API references + +# Build and Test +TODO: Describe and show how to build your code and run the tests. + +# Contribute +TODO: Explain how other users and developers can contribute to make your code better. + +If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files: +- [ASP.NET Core](https://github.com/aspnet/Home) +- [Visual Studio Code](https://github.com/Microsoft/vscode) +- [Chakra Core](https://github.com/Microsoft/ChakraCore) \ No newline at end of file diff --git a/LCU.API.Internal.FileOperations/azure-pipelines.yml b/LCU.API.Internal.FileOperations/azure-pipelines.yml new file mode 100644 index 0000000..026923a --- /dev/null +++ b/LCU.API.Internal.FileOperations/azure-pipelines.yml @@ -0,0 +1,58 @@ +# ASP.NET Core + +name: 'FileOperations.$(Build.SourceBranchName).$(Date:yyyyMMdd)$(Rev:.r)' +trigger: + branches: + include: + - '*' + +pool: + name: LCUOnPrem + demands: + - msbuild + - visualstudio + - vstest + +variables: + buildConfiguration: 'Release' + +steps: +- task: DotNetCoreCLI@2 + inputs: + command: 'restore' + feedsToUse: 'select' + vstsFeed: '58ebbac4-8e98-4281-bb3f-58003852f4a7' + +- script: dotnet build --configuration $(buildConfiguration) + displayName: 'dotnet build $(buildConfiguration)' + +- task: DotNetCoreCLI@2 + displayName: Test + inputs: + command: test + projects: '**/*Tests.csproj' + arguments: '--configuration $(BuildConfiguration) --collect "Code coverage"' + +- task: DotNetCoreCLI@2 + displayName: 'dotnet publish $(buildConfiguration)' + inputs: + command: publish + publishWebProjects: false + arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory) --framework net8.0' + zipAfterPublish: true + +- task: NuGetCommand@2 + condition: and (succeeded(), eq(variables['Build.SourceBranchName'], 'master') ) + displayName: 'NuGet push' + inputs: + command: push + packagesToPush: 'LCU.Library.Internal.FileOperationsModels/**/*.nupkg' + publishVstsFeed: '58ebbac4-8e98-4281-bb3f-58003852f4a7' + allowPackageConflicts: true + +- task: PublishBuildArtifacts@1 + condition: and (succeeded(), or( eq(variables['Build.SourceBranchName'], 'develop'), eq(variables['Build.SourceBranchName'], 'master')) ) + displayName: 'Publish Artifact: dist' + inputs: + PathtoPublish: '$(build.artifactstagingdirectory)' + ArtifactName: 'dist' \ No newline at end of file