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 |