diff --git a/LCU.API.Internal.LoanRate/.editorconfig b/LCU.API.Internal.LoanRate/.editorconfig new file mode 100644 index 0000000..a12670b --- /dev/null +++ b/LCU.API.Internal.LoanRate/.editorconfig @@ -0,0 +1,80 @@ +[*.cs] + +# CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. +dotnet_diagnostic.CS8632.severity = silent +csharp_indent_labels = one_less_than_current +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_space_around_binary_operators = before_and_after +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent + +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion diff --git a/LCU.API.Internal.LoanRate/.gitignore b/LCU.API.Internal.LoanRate/.gitignore new file mode 100644 index 0000000..40dcb4c --- /dev/null +++ b/LCU.API.Internal.LoanRate/.gitignore @@ -0,0 +1,363 @@ +## 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 +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +.idea/ +# 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 +nunit-*.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/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.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 + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# 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 +# NuGet Symbol Packages +*.snupkg +# 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 +*.appxbundle +*.appxupload + +# 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 +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# 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/ + +# CodeRush personal settings +.cr/personal + +# 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/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd diff --git a/LCU.API.Internal.LoanRate/Dockerfile b/LCU.API.Internal.LoanRate/Dockerfile new file mode 100644 index 0000000..b156384 --- /dev/null +++ b/LCU.API.Internal.LoanRate/Dockerfile @@ -0,0 +1,41 @@ +# .NET Core SDK +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS aspnetcore-build + +# Copy Projects +COPY LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate.csproj ./source/LCU.API.Internal.LoanRate/ +COPY LCU.API.Internal.LoanRateLib/LCU.API.Internal.LoanRateLib.csproj ./source/LCU.API.Internal.LoanRateLib/ +COPY LCU.Library.Internal.LoanRateModels/LCU.Library.Internal.LoanRateModels.csproj ./source/LCU.Library.Internal.LoanRateModels/ + +# Install Credential Provider and set env variables to enable Nuget restore with auth +ARG PAT=bh2lq3jibktw67b2wdbqyfln7cd3je6lk52kijntpj43v6kimpgq +RUN curl -L https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh | sh +#RUN wget -qO- https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh | bash +ENV NUGET_CREDENTIALPROVIDER_SESSIONTOKENCACHE_ENABLED true +ENV VSS_NUGET_EXTERNAL_FEED_ENDPOINTS "{\"endpointCredentials\": [{\"endpoint\":\"https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/nuget/v3/index.json\", \"password\":\"${PAT}\"}]}" + +# .NET Core Restore, use auth'd feed +RUN dotnet restore -s "https://landmarkcu.pkgs.visualstudio.com/_packaging/LCU-Internal/nuget/v3/index.json" -s "https://api.nuget.org/v3/index.json" ./source/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate.csproj + +# Copy All Files +COPY . ./source/ + +FROM aspnetcore-build AS test +LABEL test=true +COPY LCU.API.Internal.LoanRateTests/LCU.API.Internal.LoanRateTests.csproj ./source/LCU.API.Internal.LoanRateTests/ +RUN dotnet restore ./source/LCU.API.Internal.LoanRateTests/LCU.API.Internal.LoanRateTests.csproj +RUN dotnet test -c Release --results-directory /testresults --logger "trx;LogFileName=test_results.trx" ./source/LCU.API.Internal.LoanRateTests/LCU.API.Internal.LoanRateTests.csproj + +# .NET Core Publish +FROM aspnetcore-build as publish +RUN dotnet publish ./source/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate.csproj -c Release -o /dist + +# ASP.NET Core +FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.20 +RUN apk add --no-cache icu-libs && apk add --no-cache tzdata; +ENV TZ America/Chicago +ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false +ENV ASPNETCORE_ENVIRONMENT=Development + +WORKDIR /app +COPY --from=publish /dist . +ENTRYPOINT ["dotnet", "LCU.API.Internal.LoanRate.dll"] diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate.sln b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate.sln new file mode 100644 index 0000000..90fcde1 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate.sln @@ -0,0 +1,65 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33205.214 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.API.Internal.LoanRate", "LCU.API.Internal.LoanRate\LCU.API.Internal.LoanRate.csproj", "{CFB340C3-F73D-4552-801D-6B8CFA1FD70A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.API.Internal.LoanRateTests", "LCU.API.Internal.LoanRateTests\LCU.API.Internal.LoanRateTests.csproj", "{08ED9646-3A26-4ECF-AC34-39AABF46F78A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Library.Internal.LoanRateModels", "LCU.Library.Internal.LoanRateModels\LCU.Library.Internal.LoanRateModels.csproj", "{1BAA207B-92F5-4DAB-B3B8-A7887451720D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.API.Internal.LoanRateLib", "LCU.API.Internal.LoanRateLib\LCU.API.Internal.LoanRateLib.csproj", "{6FE9201A-EAE6-49FA-8F4A-D0B10022F985}" +EndProject +Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "LCU.API.Internal.LoanRateSql", "LCU.API.Internal.LoanRateSql\LCU.API.Internal.LoanRateSql.sqlproj", "{33CBC5AD-1B92-4D5F-A79F-4464C79C5F0C}" +EndProject +Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "LCU.API.Internal.LoanRateLoadTestingSql", "LCU.API.Internal.LoanRateLoadTestingSql\LCU.API.Internal.LoanRateLoadTestingSql.sqlproj", "{0687C40B-33B7-48FD-9611-C3AF96987CBE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LCU.API.Internal.LoanRateLoadTestingLib", "LCU.API.Internal.LoanRateLoadTestingLib\LCU.API.Internal.LoanRateLoadTestingLib.csproj", "{F9374890-27F2-4155-9781-674929ECE779}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CFB340C3-F73D-4552-801D-6B8CFA1FD70A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CFB340C3-F73D-4552-801D-6B8CFA1FD70A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CFB340C3-F73D-4552-801D-6B8CFA1FD70A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CFB340C3-F73D-4552-801D-6B8CFA1FD70A}.Release|Any CPU.Build.0 = Release|Any CPU + {08ED9646-3A26-4ECF-AC34-39AABF46F78A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08ED9646-3A26-4ECF-AC34-39AABF46F78A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08ED9646-3A26-4ECF-AC34-39AABF46F78A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08ED9646-3A26-4ECF-AC34-39AABF46F78A}.Release|Any CPU.Build.0 = Release|Any CPU + {1BAA207B-92F5-4DAB-B3B8-A7887451720D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BAA207B-92F5-4DAB-B3B8-A7887451720D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BAA207B-92F5-4DAB-B3B8-A7887451720D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BAA207B-92F5-4DAB-B3B8-A7887451720D}.Release|Any CPU.Build.0 = Release|Any CPU + {6FE9201A-EAE6-49FA-8F4A-D0B10022F985}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FE9201A-EAE6-49FA-8F4A-D0B10022F985}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FE9201A-EAE6-49FA-8F4A-D0B10022F985}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FE9201A-EAE6-49FA-8F4A-D0B10022F985}.Release|Any CPU.Build.0 = Release|Any CPU + {33CBC5AD-1B92-4D5F-A79F-4464C79C5F0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {33CBC5AD-1B92-4D5F-A79F-4464C79C5F0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {33CBC5AD-1B92-4D5F-A79F-4464C79C5F0C}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {33CBC5AD-1B92-4D5F-A79F-4464C79C5F0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {33CBC5AD-1B92-4D5F-A79F-4464C79C5F0C}.Release|Any CPU.Build.0 = Release|Any CPU + {33CBC5AD-1B92-4D5F-A79F-4464C79C5F0C}.Release|Any CPU.Deploy.0 = Release|Any CPU + {0687C40B-33B7-48FD-9611-C3AF96987CBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0687C40B-33B7-48FD-9611-C3AF96987CBE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0687C40B-33B7-48FD-9611-C3AF96987CBE}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {0687C40B-33B7-48FD-9611-C3AF96987CBE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0687C40B-33B7-48FD-9611-C3AF96987CBE}.Release|Any CPU.Build.0 = Release|Any CPU + {0687C40B-33B7-48FD-9611-C3AF96987CBE}.Release|Any CPU.Deploy.0 = Release|Any CPU + {F9374890-27F2-4155-9781-674929ECE779}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F9374890-27F2-4155-9781-674929ECE779}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F9374890-27F2-4155-9781-674929ECE779}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F9374890-27F2-4155-9781-674929ECE779}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5F5E7D18-CECB-4D1B-87A7-071F1037BDD6} + EndGlobalSection +EndGlobal diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/ApiConfiguration.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/ApiConfiguration.cs new file mode 100644 index 0000000..3f8195e --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/ApiConfiguration.cs @@ -0,0 +1,10 @@ +namespace LCU.API.Internal.LoanRate.Configurations; + +public class ApiConfiguration +{ + public string AppName { get; set; } + public string SeqUrl { get; set; } + public string SeqApiKey { get; set; } + public string[] CorsAllowedOrigins { get; set; } + public FeatureFlags FeatureFlags { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/FeatureFlags.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/FeatureFlags.cs new file mode 100644 index 0000000..7852b73 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/FeatureFlags.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.LoanRate.Configurations; + +public class FeatureFlags +{ + public bool RunLoadTestInitiateConsumer { get; set; } + public bool RunLoadTestProcessRateConsumer { get; set; } + public bool RunLoadTestRateCompletionConsumer { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/FileOperationsApiConfiguration.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/FileOperationsApiConfiguration.cs new file mode 100644 index 0000000..53ebcca --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/FileOperationsApiConfiguration.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations.Schema; +namespace LCU.API.Internal.LoanRate.Configurations; + +public class FileOperationsApiConfiguration +{ + public string Url { get; set; } + public string ExtractExcelWorkBookDataEndpoint { get; set; } + + [NotMapped] + public Uri ExtractExcelWorkBookDataEndpointUri => new Uri(Url + ExtractExcelWorkBookDataEndpoint); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/LoanRateLoadTestingSqlConnectionConfiguration.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/LoanRateLoadTestingSqlConnectionConfiguration.cs new file mode 100644 index 0000000..53ea358 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/LoanRateLoadTestingSqlConnectionConfiguration.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.LoanRate.Configurations; + +public class LoanRateLoadTestingSqlConnectionConfiguration +{ + public string ConnectionString { get; set; } + public int MaxRetryCount { get; set; } + public TimeSpan MaxRetryDelay { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/LoanRateSqlConnectionConfiguration.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/LoanRateSqlConnectionConfiguration.cs new file mode 100644 index 0000000..000b0ae --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/LoanRateSqlConnectionConfiguration.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.LoanRate.Configurations; + +public class LoanRateSqlConnectionConfiguration +{ + public string ConnectionString { get; set; } + public int MaxRetryCount { get; set; } + public TimeSpan MaxRetryDelay { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/ReceiveEndpointConfigurations.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/ReceiveEndpointConfigurations.cs new file mode 100644 index 0000000..b073eb3 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Configurations/ReceiveEndpointConfigurations.cs @@ -0,0 +1,9 @@ +using LCU.Library.Internal.LcuMassTransitModels; +namespace LCU.API.Internal.LoanRate.Configurations; + +public class ReceiveEndpointConfigurations +{ + public ReceiveEndpointConfiguration LoadTestInitiateReceiveEndpoint { get; set; } + public ReceiveEndpointConfiguration LoadTestProcessRateReceiveEndpoint { get; set; } + public ReceiveEndpointConfiguration LoadTestRateCompletionReceiveEndpoint { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Consumers/LoadTestInitiateConsumer.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Consumers/LoadTestInitiateConsumer.cs new file mode 100644 index 0000000..c628488 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Consumers/LoadTestInitiateConsumer.cs @@ -0,0 +1,42 @@ +using MassTransit; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +using LCU.Api.Internal.MaidBotLib.Models; +using LCU.API.Internal.LoanRate.Services.LoadTestingServices; +namespace LCU.API.Internal.LoanRate.Consumers; + +public class LoadTestInitiateConsumer : IConsumer +{ + private readonly IServiceScopeFactory _scopeFactory; + + public LoadTestInitiateConsumer(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + } + + public async Task Consume(ConsumeContext context) + { + try + { + Log.Debug("Starting HandleLoadTestInitiation."); + using var scope = _scopeFactory.CreateScope(); + await Task.Run(async () => await scope.ServiceProvider.GetRequiredService().HandleLoadTestInitiation(context.Message)); + Log.Debug("Starting HandleLoadTestInitiation."); + } + catch (ServiceException serviceException) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), + Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Error(serviceException, $"Load Test Initiation Failure. " + + $"The LoadTestInitiateRequest is placed on LCU.LoanRate.LoadTest.Initiate_error."); + throw; + } + catch (Exception exception) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), + Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Fatal(exception, $"Unexpected Load Test Initiation Failure. " + + $"The LoadTestInitiateRequest is placed on LCU.LoanRate.LoadTest.Initiate_error."); + throw; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Consumers/LoadTestProcessRateConsumer.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Consumers/LoadTestProcessRateConsumer.cs new file mode 100644 index 0000000..a5c3d42 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Consumers/LoadTestProcessRateConsumer.cs @@ -0,0 +1,42 @@ +using MassTransit; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +using LCU.API.Internal.LoanRate.Services.LoadTestingServices; +using LCU.Api.Internal.MaidBotLib.Models; +namespace LCU.API.Internal.LoanRate.Consumers; + +public class LoadTestProcessRateConsumer : IConsumer +{ + private readonly IServiceScopeFactory _scopeFactory; + + public LoadTestProcessRateConsumer(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + } + + public async Task Consume(ConsumeContext context) + { + try + { + Log.Debug("Starting HandleLoadTestProcessRate."); + using var scope = _scopeFactory.CreateScope(); + await Task.Run(async () => await scope.ServiceProvider.GetRequiredService().HandleLoadTestProcessRate(context.Message)); + Log.Debug("Starting HandleLoadTestProcessRate."); + } + catch (ServiceException serviceException) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), + Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Error(serviceException, $"Load Test Process Rate Failure. " + + $"The LoadTestProcessRateRequest is placed on LCU.LoanRate.LoadTest.ProcessRate_error."); + throw; + } + catch (Exception exception) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), + Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Fatal(exception, $"Unexpected Load Test Process Rate Failure. " + + $"The LoadTestProcessRateRequest is placed on LCU.LoanRate.LoadTest.ProcessRate_error."); + throw; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Consumers/LoadTestRateCompletionConsumer.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Consumers/LoadTestRateCompletionConsumer.cs new file mode 100644 index 0000000..23349d8 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Consumers/LoadTestRateCompletionConsumer.cs @@ -0,0 +1,50 @@ +using MassTransit; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +using LCU.Api.Internal.MaidBotLib.Models; +using LCU.API.Internal.LoanRate.Services.LoadTestingServices; +namespace LCU.API.Internal.LoanRate.Consumers; + +public class LoadTestRateCompletionConsumer : IConsumer> +{ + private readonly IServiceScopeFactory _scopeFactory; + + public LoadTestRateCompletionConsumer(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + } + + public async Task Consume(ConsumeContext> context) + { + try + { + Log.Debug("Starting HandleLoadTestRateCompletion."); + using var scope = _scopeFactory.CreateScope(); + + var loadTestRateCompletionRequests = new List(); + for (int index = 0; index < context.Message.Length; index++) + { + ConsumeContext messageContext = context.Message[index]; + loadTestRateCompletionRequests.Add(messageContext.Message); + } + + await Task.Run(async () => await scope.ServiceProvider.GetRequiredService().HandleLoadTestRateCompletion(loadTestRateCompletionRequests)); + Log.Debug("Starting HandleLoadTestRateCompletion."); + } + catch (ServiceException serviceException) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), + Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Error(serviceException, $"Load Test Rate Completion Failure. " + + $"The LoadTestRateCompletionRequest is placed on LCU.LoanRate.LoadTest.RateCompletion_error."); + throw; + } + catch (Exception exception) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), + Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Fatal(exception, $"Unexpected Load Test Rate Completion Failure. " + + $"The LoadTestRateCompletionRequest is placed on LCU.LoanRate.LoadTest.RateCompletion_error."); + throw; + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/ComplexEvaluationDetailController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/ComplexEvaluationDetailController.cs new file mode 100644 index 0000000..a289481 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/ComplexEvaluationDetailController.cs @@ -0,0 +1,126 @@ +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.LcuEvaluationModels.Entities; +using LCU.Library.Internal.LcuEvaluationModels.Requests; +using Microsoft.AspNetCore.Mvc; + +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class ComplexEvaluationDetailController : ControllerBase +{ + private readonly IComplexEvaluationDetailService _complexEvaluationDetailService; + + public ComplexEvaluationDetailController(IComplexEvaluationDetailService complexEvaluationDetailService) + { + _complexEvaluationDetailService = complexEvaluationDetailService; + } + + [HttpPost("")] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(EvaluationDetail), 400)] + [ProducesResponseType(500)] + public async Task LoanRatePostComplex([FromBody] EvaluationDetailRequest request) + { + try + { + if (request is null) + { + return BadRequest("Request cannot be null."); + } + + var result = await _complexEvaluationDetailService.SaveComplexAsync(request); + return CreatedAtAction("GetById", new { controller = "EvaluationDetail", id = result.EvaluationDetailId }, result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + /// + /// Updates the EvaluationDetail, as well as the Create/Edit/Delete of the EvaluationParameter child collection. + /// + /// + /// + [HttpPut("")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task LoanRatePutComplex([FromBody] EvaluationDetail evaluationDetail) + { + try + { + if (evaluationDetail is null) + { + return BadRequest("EvaluationDetail cannot be null."); + } + + if (evaluationDetail.EvaluationDetailId == 0) + { + return BadRequest("EvaluationDetailId cannot be empty."); + } + + + if (!evaluationDetail.EvaluationParameters.Any()) + { + return BadRequest("EvaluationParameters cannot be empty."); + } + + var result = await _complexEvaluationDetailService.UpdateComplexAsync(evaluationDetail); + if (result is null) + { + return StatusCode(StatusCodes.Status406NotAcceptable); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + /// + /// Deletes the EvaluationDetail, as well as removes the end date of the previous version. + /// + /// + /// + [HttpDelete("{id:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task LoanRateDeleteComplex(int id) + { + try + { + if (id <= 0) + { + return BadRequest($"EvaluationDetailId {id} is invalid."); + } + + await _complexEvaluationDetailService.DeleteComplexAsync(id); + return Ok(); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/LoadTestInitiateController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/LoadTestInitiateController.cs new file mode 100644 index 0000000..b2f75a0 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/LoadTestInitiateController.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +using LCU.API.Internal.LoanRate.Services.QueueServices; +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class LoadTestInitiateController : ControllerBase +{ + private readonly IQueueLoadTestInitiateService _queueLoadTestInitiateService; + + public LoadTestInitiateController(IQueueLoadTestInitiateService queueLoadTestInitiateService) + { + _queueLoadTestInitiateService = queueLoadTestInitiateService; + } + + [HttpPost("QueueLoadTestInitiateRequest")] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public async Task QueueLoadTestInitiateRequest([FromBody] LoadTestInitiateRequest request) + { + try + { + await _queueLoadTestInitiateService.QueueLoadTestInitiateRequest(request); + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/LoadTestRunInfoController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/LoadTestRunInfoController.cs new file mode 100644 index 0000000..6982070 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/LoadTestRunInfoController.cs @@ -0,0 +1,312 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.LCUFilterModels.Pagination; +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; + +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class LoadTestRunInfoController : ControllerBase +{ + private readonly ILoadTestRunInfoService _loadTestRunInfoService; + + public LoadTestRunInfoController(ILoadTestRunInfoService loadTestRunInfoService) + { + _loadTestRunInfoService = loadTestRunInfoService; + } + + [HttpGet] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(500)] + public async Task Get() + { + try + { + var result = await _loadTestRunInfoService.GetAsync(); + if (result is null || !result.Any()) + { + return NoContent(); + } + + 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("{id:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(LoadTestRunInfo), 400)] + [ProducesResponseType(500)] + public async Task GetById(int id) + { + try + { + if (id <= 0) + { + return BadRequest($"Id {id} is invalid."); + } + + var result = await _loadTestRunInfoService.GetByIdAsync(id); + if (result is null) + { + return NoContent(); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("GetListById")] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(IEnumerable), 400)] + [ProducesResponseType(500)] + public async Task GetListById([FromBody] List ids) + { + try + { + if (ids is null || !ids.Any()) + { + return BadRequest("List of ids are invalid."); + } + + var result = await _loadTestRunInfoService.GetRangeByIdAsync(ids); + if (result == null) + { + return NoContent(); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("GetFilteredLoadTestRunInfo")] + [ProducesResponseType(typeof(PagedResponse), 200)] + [ProducesResponseType(500)] + public async Task GetFilteredLoadTestRunInfo([FromBody] LoadTestRunInfoFilter loadTestRunInfoFilter) + { + try + { + var filterResult = await _loadTestRunInfoService.GetFilteredLoadTestRunInfo(loadTestRunInfoFilter); + var result = new PagedResponse(filterResult, loadTestRunInfoFilter); + return Ok(result); + } + catch (ServiceException) + { + return StatusCode(StatusCodes.Status500InternalServerError); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(LoadTestRunInfo), 400)] + [ProducesResponseType(500)] + public async Task Post([FromBody] LoadTestRunInfo loadTestRunInfo) + { + try + { + if (loadTestRunInfo is null) + { + return BadRequest("LoadTestRunInfo cannot be null."); + } + + var result = await _loadTestRunInfoService.SaveAsync(loadTestRunInfo); + return CreatedAtAction(nameof(GetById), new { id = result.LoadTestRunInfoId }, result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("SaveRange")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(IEnumerable), 400)] + [ProducesResponseType(500)] + public async Task SaveRange([FromBody] List loadTestRunInfos) + { + try + { + if (loadTestRunInfos is null || !loadTestRunInfos.Any()) + { + return BadRequest("LoadTestRunInfos cannot be null or empty."); + } + + await _loadTestRunInfoService.SaveRangeAsync(loadTestRunInfos); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPut] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task Put([FromBody] LoadTestRunInfo loadTestRunInfo) + { + try + { + if (loadTestRunInfo is null) + { + return BadRequest("LoadTestRunInfo cannot be null."); + } + + if (loadTestRunInfo.LoadTestRunInfoId == 0) + { + return BadRequest("LoadTestRunInfoId cannot be empty."); + } + + var result = await _loadTestRunInfoService.UpdateAsync(loadTestRunInfo); + if (result is null) + { + return StatusCode(StatusCodes.Status406NotAcceptable); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPut("UpdateRange")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task UpdateRange([FromBody] List loadTestRunInfos) + { + try + { + if (loadTestRunInfos is null || !loadTestRunInfos.Any()) + { + return BadRequest("LoadTestRunInfos cannot be null or empty."); + } + + if (loadTestRunInfos.Any(loadTestRunInfo => loadTestRunInfo.LoadTestRunInfoId == 0)) + { + return BadRequest("LoadTestRunInfoIds cannot be empty."); + } + + var result = await _loadTestRunInfoService.UpdateRangeAsync(loadTestRunInfos); + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpDelete("{id:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task Delete(int id) + { + try + { + if (id <= 0) + { + return BadRequest($"LoadTestRunInfoId {id} is invalid."); + } + + await _loadTestRunInfoService.DeleteAsync(id); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpDelete("DeleteRange")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task DeleteRange([FromBody] List ids) + { + try + { + if (ids is null || !ids.Any()) + { + return BadRequest("List of ids are invalid."); + } + + await _loadTestRunInfoService.DeleteRangeAsync(ids); + return Ok(); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateController.cs new file mode 100644 index 0000000..0005bab --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateController.cs @@ -0,0 +1,341 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.API.Internal.LoanRate.Services; +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class RateController : ControllerBase +{ + private readonly IRateService _rateService; + + public RateController(IRateService rateService) + { + _rateService = rateService; + } + + [HttpGet] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(500)] + public async Task Get() + { + try + { + var result = await _rateService.GetAsync(); + if (result is null || !result.Any()) + { + return NoContent(); + } + + 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("{id:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(Rate), 400)] + [ProducesResponseType(500)] + public async Task GetById(int id) + { + try + { + if (id <= 0) + { + return BadRequest($"Id {id} is invalid."); + } + + var result = await _rateService.GetByIdAsync(id); + if (result is null) + { + return NoContent(); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("GetListById")] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(IEnumerable), 400)] + [ProducesResponseType(500)] + public async Task GetListById([FromBody] List ids) + { + try + { + if (ids is null || !ids.Any()) + { + return BadRequest("List of ids are invalid."); + } + + var result = await _rateService.GetRangeByIdAsync(ids); + if (result == null) + { + return NoContent(); + } + + 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("GetByLoadTestRunInfo/{loadTestRunInfoId:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(IEnumerable), 400)] + [ProducesResponseType(500)] + public async Task GetByLoadTestRunInfo([FromRoute] int loadTestRunInfoId) + { + try + { + if (loadTestRunInfoId == 0) + { + return BadRequest("Id is required"); + } + + var result = await _rateService.GetByLoadTestRunInfoId(loadTestRunInfoId); + if (result == null) + { + return NoContent(); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(Rate), 400)] + [ProducesResponseType(500)] + public async Task Post([FromBody] Rate rate) + { + try + { + if (rate is null) + { + return BadRequest("Rate cannot be null."); + } + + var result = await _rateService.SaveAsync(rate); + return CreatedAtAction(nameof(GetById), new { id = result.RateId }, result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("SaveRange")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(IEnumerable), 400)] + [ProducesResponseType(500)] + public async Task SaveRange([FromBody] List rates) + { + try + { + if (rates is null || !rates.Any()) + { + return BadRequest("Rates cannot be null or empty."); + } + + await _rateService.SaveRangeAsync(rates); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPut] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task Put([FromBody] Rate rate) + { + try + { + if (rate is null) + { + return BadRequest("Rate cannot be null."); + } + + if (rate.RateId == 0) + { + return BadRequest("RateId cannot be empty."); + } + + var result = await _rateService.UpdateAsync(rate); + if (result is null) + { + return StatusCode(StatusCodes.Status406NotAcceptable); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPut("UpdateRange")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task UpdateRange([FromBody] List rates) + { + try + { + if (rates is null || !rates.Any()) + { + return BadRequest("Rates cannot be null or empty."); + } + + if (rates.Any(rate => rate.RateId == 0)) + { + return BadRequest("RateIds cannot be empty."); + } + + var result = await _rateService.UpdateRangeAsync(rates); + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpDelete("{id:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task Delete(int id) + { + try + { + if (id <= 0) + { + return BadRequest($"RateId {id} is invalid."); + } + + await _rateService.DeleteAsync(id); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpDelete("DeleteRange")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task DeleteRange([FromBody] List ids) + { + try + { + if (ids is null || !ids.Any()) + { + return BadRequest("List of ids are invalid."); + } + + await _rateService.DeleteRangeAsync(ids); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpDelete("ClearCache")] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public IActionResult ClearCache() + { + try + { + _rateService.ClearCache(); + return Ok(); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateEvaluationController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateEvaluationController.cs new file mode 100644 index 0000000..ed5f282 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateEvaluationController.cs @@ -0,0 +1,313 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.API.Internal.LoanRate.Services; +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class RateEvaluationController : ControllerBase +{ + private readonly IRateEvaluationService _rateEvaluationService; + + public RateEvaluationController(IRateEvaluationService rateEvaluationService) + { + _rateEvaluationService = rateEvaluationService; + } + + [HttpGet] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(500)] + public async Task Get() + { + try + { + var result = await _rateEvaluationService.GetAsync(); + if (result is null || !result.Any()) + { + return NoContent(); + } + + 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("{id:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(RateEvaluation), 400)] + [ProducesResponseType(500)] + public async Task GetById(int id) + { + try + { + if (id <= 0) + { + return BadRequest($"Id {id} is invalid."); + } + + var result = await _rateEvaluationService.GetByIdAsync(id); + if (result is null) + { + return NoContent(); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("GetListById")] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(IEnumerable), 400)] + [ProducesResponseType(500)] + public async Task GetListById([FromBody] List ids) + { + try + { + if (ids is null || !ids.Any()) + { + return BadRequest("List of ids are invalid."); + } + + var result = await _rateEvaluationService.GetRangeByIdAsync(ids); + if (result == null) + { + return NoContent(); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(RateEvaluation), 400)] + [ProducesResponseType(500)] + public async Task Post([FromBody] RateEvaluation rateEvaluation) + { + try + { + if (rateEvaluation is null) + { + return BadRequest("RateEvaluation cannot be null."); + } + + var result = await _rateEvaluationService.SaveAsync(rateEvaluation); + return CreatedAtAction(nameof(GetById), new { id = result.RateEvaluationId }, result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("SaveRange")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(IEnumerable), 400)] + [ProducesResponseType(500)] + public async Task SaveRange([FromBody] List rateEvaluations) + { + try + { + if (rateEvaluations is null || !rateEvaluations.Any()) + { + return BadRequest("RateEvaluations cannot be null or empty."); + } + + await _rateEvaluationService.SaveRangeAsync(rateEvaluations); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPut] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task Put([FromBody] RateEvaluation rateEvaluation) + { + try + { + if (rateEvaluation is null) + { + return BadRequest("RateEvaluation cannot be null."); + } + + if (rateEvaluation.RateEvaluationId == 0) + { + return BadRequest("RateEvaluationId cannot be empty."); + } + + var result = await _rateEvaluationService.UpdateAsync(rateEvaluation); + if (result is null) + { + return StatusCode(StatusCodes.Status406NotAcceptable); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPut("UpdateRange")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task UpdateRange([FromBody] List rateEvaluations) + { + try + { + if (rateEvaluations is null || !rateEvaluations.Any()) + { + return BadRequest("RateEvaluations cannot be null or empty."); + } + + if (rateEvaluations.Any(rateEvaluation => rateEvaluation.RateEvaluationId == 0)) + { + return BadRequest("RateEvaluationIds cannot be empty."); + } + + var result = await _rateEvaluationService.UpdateRangeAsync(rateEvaluations); + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpDelete("{id:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task Delete(int id) + { + try + { + if (id <= 0) + { + return BadRequest($"RateEvaluationId {id} is invalid."); + } + + await _rateEvaluationService.DeleteAsync(id); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpDelete("DeleteRange")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task DeleteRange([FromBody] List ids) + { + try + { + if (ids is null || !ids.Any()) + { + return BadRequest("List of ids are invalid."); + } + + await _rateEvaluationService.DeleteRangeAsync(ids); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpGet("CountByEvaluationDetailId/{evaluationDetailId:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task CountByEvaluationDetailId(int evaluationDetailId) + { + try + { + if (evaluationDetailId <= 0) + { + return BadRequest($"EvaluationDetailId {evaluationDetailId} is invalid."); + } + + return Ok(await _rateEvaluationService.CountByEvaluationDetailIdAsync(evaluationDetailId)); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateFinderController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateFinderController.cs new file mode 100644 index 0000000..8a4bc71 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateFinderController.cs @@ -0,0 +1,56 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class RateFinderController : ControllerBase +{ + private readonly IRateFinderService _rateFinderService; + + public RateFinderController(IRateFinderService rateFinderService) + { + _rateFinderService = rateFinderService; + } + + [HttpPost("FindRate")] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public async Task FindRate([FromBody] RateRequest request) + { + try + { + return Ok(await _rateFinderService.FindRate(request)); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("FindRateObject")] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public async Task FindRateObject([FromBody] RateRequest request) + { + try + { + return Ok(await _rateFinderService.FindRateObject(request)); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateProcessingInfoController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateProcessingInfoController.cs new file mode 100644 index 0000000..10a277f --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateProcessingInfoController.cs @@ -0,0 +1,288 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class RateProcessingInfoController : ControllerBase +{ + private readonly IRateProcessingInfoService _rateProcessingInfoService; + + public RateProcessingInfoController(IRateProcessingInfoService rateProcessingInfoService) + { + _rateProcessingInfoService = rateProcessingInfoService; + } + + [HttpGet] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(500)] + public async Task Get() + { + try + { + var result = await _rateProcessingInfoService.GetAsync(); + if (result is null || !result.Any()) + { + return NoContent(); + } + + 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("{id:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(RateProcessingInfo), 400)] + [ProducesResponseType(500)] + public async Task GetById(int id) + { + try + { + if (id <= 0) + { + return BadRequest($"Id {id} is invalid."); + } + + var result = await _rateProcessingInfoService.GetByIdAsync(id); + if (result is null) + { + return NoContent(); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("GetListById")] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(IEnumerable), 400)] + [ProducesResponseType(500)] + public async Task GetListById([FromBody] List ids) + { + try + { + if (ids is null || !ids.Any()) + { + return BadRequest("List of ids are invalid."); + } + + var result = await _rateProcessingInfoService.GetRangeByIdAsync(ids); + if (result == null) + { + return NoContent(); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost] + [ProducesResponseType(201)] + [ProducesResponseType(typeof(RateProcessingInfo), 400)] + [ProducesResponseType(500)] + public async Task Post([FromBody] RateProcessingInfo rateProcessingInfo) + { + try + { + if (rateProcessingInfo is null) + { + return BadRequest("RateProcessingInfo cannot be null."); + } + + var result = await _rateProcessingInfoService.SaveAsync(rateProcessingInfo); + return CreatedAtAction(nameof(GetById), new { id = result.RateProcessingInfoId }, result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("SaveRange")] + [ProducesResponseType(200)] + [ProducesResponseType(typeof(IEnumerable), 400)] + [ProducesResponseType(500)] + public async Task SaveRange([FromBody] List rateProcessingInfos) + { + try + { + if (rateProcessingInfos is null || !rateProcessingInfos.Any()) + { + return BadRequest("RateProcessingInfos cannot be null or empty."); + } + + await _rateProcessingInfoService.SaveRangeAsync(rateProcessingInfos); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPut] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task Put([FromBody] RateProcessingInfo rateProcessingInfo) + { + try + { + if (rateProcessingInfo is null) + { + return BadRequest("RateProcessingInfo cannot be null."); + } + + if (rateProcessingInfo.RateProcessingInfoId == 0) + { + return BadRequest("RateProcessingInfoId cannot be empty."); + } + + var result = await _rateProcessingInfoService.UpdateAsync(rateProcessingInfo); + if (result is null) + { + return StatusCode(StatusCodes.Status406NotAcceptable); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPut("UpdateRange")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public async Task UpdateRange([FromBody] List rateProcessingInfos) + { + try + { + if (rateProcessingInfos is null || !rateProcessingInfos.Any()) + { + return BadRequest("RateProcessingInfos cannot be null or empty."); + } + + if (rateProcessingInfos.Any(rateProcessingInfo => rateProcessingInfo.RateProcessingInfoId == 0)) + { + return BadRequest("RateProcessingInfoIds cannot be empty."); + } + + var result = await _rateProcessingInfoService.UpdateRangeAsync(rateProcessingInfos); + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpDelete("{id:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task Delete(int id) + { + try + { + if (id <= 0) + { + return BadRequest($"RateProcessingInfoId {id} is invalid."); + } + + await _rateProcessingInfoService.DeleteAsync(id); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpDelete("DeleteRange")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task DeleteRange([FromBody] List ids) + { + try + { + if (ids is null || !ids.Any()) + { + return BadRequest("List of ids are invalid."); + } + + await _rateProcessingInfoService.DeleteRangeAsync(ids); + return Ok(); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateSheetCellController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateSheetCellController.cs new file mode 100644 index 0000000..b07e4ef --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateSheetCellController.cs @@ -0,0 +1,17 @@ +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.LcuClassPatterns.Crud.Controllers; +using Microsoft.AspNetCore.Mvc; + +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class RateSheetCellController : CrudController +{ + private readonly IRateSheetCellService _rateSheetCellService; + + public RateSheetCellController(IRateSheetCellService rateSheetCellService) : base(rateSheetCellService) + { + _rateSheetCellService = rateSheetCellService; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateSheetCellUploaderController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateSheetCellUploaderController.cs new file mode 100644 index 0000000..f11e76e --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateSheetCellUploaderController.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class RateSheetCellUploaderController : ControllerBase +{ + private readonly IRateSheetCellUploaderService _rateSheetCellUploaderService; + + public RateSheetCellUploaderController(IRateSheetCellUploaderService rateSheetCellUploaderService) + { + _rateSheetCellUploaderService = rateSheetCellUploaderService; + } + + [HttpPost("UploadRateSheetCells")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task UploadRateSheetCells(RateSheetCellUploadRequest request) + { + try + { + if (request is null) + { + return BadRequest("RateSheetCellUploadRequest is required."); + } + + var result = await _rateSheetCellUploaderService.UploadRateSheetCellsAsync(request); + if (result is null) + { + return NoContent(); + } + + return Ok(result); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateSheetController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateSheetController.cs new file mode 100644 index 0000000..4980f71 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateSheetController.cs @@ -0,0 +1,72 @@ +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.LcuClassPatterns.Crud.Controllers; +using LCU.Library.Internal.LoanRateModels.Requests; +using Microsoft.AspNetCore.Mvc; + +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class RateSheetController : CrudController +{ + private readonly IRateSheetService _rateSheetService; + + public RateSheetController(IRateSheetService rateSheetService) : base(rateSheetService) + { + _rateSheetService = rateSheetService; + } + + [HttpPost("GetByDateAndRateType")] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(Rate), 400)] + [ProducesResponseType(500)] + public async Task GetByDateAndRateType([FromBody] RateSheetRequest request) + { + try + { + if (request is null) + { + return BadRequest("Request cannot be null."); + } + + var result = await _rateSheetService.GetByDateAndRateTypeAsync( request.EffectiveDate, request.RateType); + if (result is null) + { + return NoContent(); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpDelete("ClearCache")] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public IActionResult ClearCache() + { + try + { + _rateSheetService.ClearCache(); + return Ok(); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateUploadInfoController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateUploadInfoController.cs new file mode 100644 index 0000000..c22992f --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateUploadInfoController.cs @@ -0,0 +1,123 @@ +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.LcuClassPatterns.Crud.Controllers; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LoanRateModels.Responses; +using Microsoft.AspNetCore.Mvc; + +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class RateUploadInfoController : CrudController +{ + private readonly IRateUploadInfoService _rateUploadInfoService; + + public RateUploadInfoController(IRateUploadInfoService rateUploadInfoService) : base(rateUploadInfoService) + { + _rateUploadInfoService = rateUploadInfoService; + } + + [HttpGet("GetLatest")] + [ProducesResponseType(typeof(RateUploadInfo), 200)] + [ProducesResponseType(204)] + [ProducesResponseType(500)] + public async Task GetLatest() + { + try + { + var result = await _rateUploadInfoService.GetLatestAsync(); + + if (result is null) + { + return NoContent(); + } + + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("GetFilteredRateUploadInfo")] + [ProducesResponseType(typeof(RateUploadInfoFilterResponse), 200)] + [ProducesResponseType(500)] + public async Task GetFilteredRateUploadInfo([FromBody] RateUploadInfoFilter rateUploadInfoFilter) + { + try + { + var filterResult = await _rateUploadInfoService.GetFilteredRateUploadInfo(rateUploadInfoFilter); + var result = new RateUploadInfoFilterResponse(filterResult, rateUploadInfoFilter); + return Ok(result); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpDelete("ComplexDelete/{id:int}")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task ComplexDelete(int id) + { + try + { + if (id == 0) + { + return BadRequest("RateProcessingInfoId is required."); + } + + await _rateUploadInfoService.ComplexDelete(id); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("ComplexSave")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task ComplexSave(RateUploadInfoRequest request) + { + try + { + if (request is null) + { + return BadRequest("RateProcessingInfoRequest is required."); + } + + var result = await _rateUploadInfoService.ComplexSave(request); + return Ok(result); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateUploaderController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateUploaderController.cs new file mode 100644 index 0000000..95a6d42 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateUploaderController.cs @@ -0,0 +1,92 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Requests; +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class RateUploaderController : ControllerBase +{ + private readonly IRateUploaderService _rateUploaderService; + + public RateUploaderController(IRateUploaderService rateUploaderService) + { + _rateUploaderService = rateUploaderService; + } + + [HttpPost("UploadRates")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task UploadRates(RateUploadRequest request) + { + try + { + if (request is null) + { + return BadRequest("RateUploadRequest is required."); + } + + await _rateUploaderService.UploadRates(request); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("UploadRatesByExcelDataRequest")] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public async Task UploadRates([FromBody] RateUploadExcelDataRequest request) + { + try + { + await _rateUploaderService.UploadRates(request); + return Ok(); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("InitiateRatesRulesRequest")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] + [ProducesResponseType(500)] + public async Task InitiateRatesRulesRequest([FromBody] IEnumerable requests) + { + try + { + if (requests is null || !requests.Any()) + { + return BadRequest("Request can't be null."); + } + + var result = await _rateUploaderService.InitiateRatesEvaluations(requests); + return Ok(result); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateValidationController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateValidationController.cs new file mode 100644 index 0000000..b715658 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/RateValidationController.cs @@ -0,0 +1,54 @@ +using LCU.API.Internal.LoanRate.Services; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +using LCU.Library.Internal.LcuClassPatterns.Crud.Controllers; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LoanRateModels.Responses.Validation; +using LCU.Library.Internal.LoanRateModels.Responses.Validation.RateSheetValidations; +using Microsoft.AspNetCore.Mvc; + +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class RateValidationController : ControllerBase +{ + private readonly IRateValidationService _rateValidationService; + + public RateValidationController(IRateValidationService rateValidationService) + { + _rateValidationService = rateValidationService; + } + + [HttpPost("Validate")] + [ProducesResponseType(200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(RateSheetValidationResult), 400)] + [ProducesResponseType(500)] + public async Task Validate([FromBody] List rates) + { + try + { + if (rates is null || rates.Count == 0) + { + return BadRequest("Rates cannot be null."); + } + + var result = await _rateValidationService.ValidateAsync(rates); + if (result is null) + { + return NoContent(); + } + + return Ok(result); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/StatusController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/StatusController.cs new file mode 100644 index 0000000..8855519 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/StatusController.cs @@ -0,0 +1,34 @@ +using LCU.API.Internal.LoanRate.Services; +using Microsoft.AspNetCore.Mvc; +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class StatusController : ControllerBase +{ + private readonly IStatusService _statusService; + public StatusController(IStatusService statusService) + { + _statusService = statusService; + } + + [HttpGet] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public IActionResult Get() + { + try + { + return Ok(_statusService.GetEnviromentView()); + } + 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.LoanRate/LCU.API.Internal.LoanRate/Controllers/ValidateRateEvaluationController.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/ValidateRateEvaluationController.cs new file mode 100644 index 0000000..c4c84da --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Controllers/ValidateRateEvaluationController.cs @@ -0,0 +1,106 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LcuEvaluation.Services.EvaluationServices; +using LCU.API.Internal.LoanRate.Services.ValidateLoanRateServices; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +using LCU.Library.Internal.LcuEvaluationModels.Enums; + +namespace LCU.API.Internal.LoanRate.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class ValidateRateEvaluationsController : ControllerBase +{ + private readonly IValidateRateEvaluationsService _validateRateEvaluationsService; + private readonly IRateFinderService _rateFinderService; + private readonly IExecuteEvaluationService _executeEvaluationService; + + public ValidateRateEvaluationsController(IValidateRateEvaluationsService validateRateEvaluationsService, + IRateFinderService rateFinderService, + IExecuteEvaluationService executeEvaluationService) + { + _validateRateEvaluationsService = validateRateEvaluationsService; + _rateFinderService = rateFinderService; + _executeEvaluationService = executeEvaluationService; + } + + [HttpPost("ValidateRatePassesEvaluation")] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public async Task ValidateRatePassesEvaluation([FromBody] RateRequest request, int rateId, int evaluationDetailId) + { + try + { + return Ok(await _validateRateEvaluationsService.ValidateRatePassesEvaluation(request, rateId, evaluationDetailId)); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("ValidateRatePassesEvaluations")] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public async Task ValidateRatePassesEvaluations([FromBody] RateRequest request, int rateId) + { + try + { + return Ok(await _validateRateEvaluationsService.ValidateRatePassesEvaluations(request, rateId)); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("DetermineRatePassedRules")] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public async Task DetermineVehicleRatePassedRules([FromBody] RateRequest request) + { + try + { + return Ok(await _rateFinderService.DeterminePassedRules(request)); + } + catch (ServiceException exception) + { + return StatusCode(StatusCodes.Status500InternalServerError, exception.Message); + } + catch (Exception exception) + { + Log.Error(exception, "Unexpected Exception"); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("RawEvaluationTest")] + [ProducesResponseType(200)] + [ProducesResponseType(500)] + public IActionResult RawEvaluationTest(string evaluationCondition, EvaluationReturnTypes evaluationReturnType) + { + try + { + return Ok(_executeEvaluationService.ExecuteCondition(evaluationCondition, evaluationReturnType)); + } + 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.LoanRate/LCU.API.Internal.LoanRate/DataAccess/FileOperationsApiDao.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/DataAccess/FileOperationsApiDao.cs new file mode 100644 index 0000000..75d77da --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/DataAccess/FileOperationsApiDao.cs @@ -0,0 +1,23 @@ +using LCU.Library.Internal.LcuHttpClient.HttpRequest; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Requests; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Responses; +using LCU.API.Internal.LoanRate.Configurations; +namespace LCU.API.Internal.LoanRate.DataAccess; + +public class FileOperationsApiDao : IFileOperationsApiDao +{ + private readonly FileOperationsApiConfiguration _fileOperationsApiConfiguration; + private readonly IPostRequest _postRequest; + + public FileOperationsApiDao(FileOperationsApiConfiguration fileOperationsApiConfiguration, IPostRequest postRequest) + { + _fileOperationsApiConfiguration = fileOperationsApiConfiguration; + _postRequest = postRequest; + } + + public async Task DirectExtractExcelWorkBookData(ExcelDataRequest request) + { + return await _postRequest.Async(_fileOperationsApiConfiguration.ExtractExcelWorkBookDataEndpointUri, + request); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/DataAccess/IFileOperationsApiDao.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/DataAccess/IFileOperationsApiDao.cs new file mode 100644 index 0000000..510ac38 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/DataAccess/IFileOperationsApiDao.cs @@ -0,0 +1,8 @@ +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Requests; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Responses; +namespace LCU.API.Internal.LoanRate.DataAccess; + +public interface IFileOperationsApiDao +{ + Task DirectExtractExcelWorkBookData(ExcelDataRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate.csproj b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate.csproj new file mode 100644 index 0000000..f01e4ca --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate.csproj @@ -0,0 +1,42 @@ + + + net8.0 + enable + true + + + 1701;1702;8618;1591 + False + + + 1701;1702;8618;1591 + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Program.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Program.cs new file mode 100644 index 0000000..822dd8f --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Program.cs @@ -0,0 +1,328 @@ +using System.Text.Json.Serialization; +using Microsoft.OpenApi.Models; +using Microsoft.EntityFrameworkCore; +using MassTransit; +using Newtonsoft.Json; +using LCU.Library.Internal.Configuration; +using LCU.Library.Internal.Logging; +using LCU.Library.Internal.Logging.Middleware; +using LCU.Library.Internal.Logging.Models; +using LCU.Library.Internal.Status; +using LCU.Library.Internal.LcuMassTransitModels; +using LCU.Library.Internal.LcuMassTransit; +using LCU.Library.Internal.LcuHttpClient; +using LCU.API.Internal.LoanRate.Configurations; +using LoadTestingLibAppDbContext = LCU.API.Internal.LoanRateLoadTestingLib.Persistence.AppDbContext; +using LoadTestingLibRepository = LCU.API.Internal.LoanRateLoadTestingLib.Repositories; +using LCU.API.Internal.LoanRateLoadTestingLib.Repositories; +using LCU.API.Internal.LoanRate.Services; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +using LoanRateLibRepository = LCU.API.Internal.LoanRateLib.Repositories; +using LoanRateLibPersistence = LCU.API.Internal.LoanRateLib.Persistence; +using LCU.API.Internal.LoanRateLib.Repositories; +using LCU.API.Internal.LoanRate.Services.ValidateLoanRateServices; +using LCU.API.Internal.LoanRate.Consumers; +using LCU.API.Internal.LoanRate.Services.LoadTestingServices; +using LCU.API.Internal.LoanRate.Services.QueueServices; +using LCU.API.Internal.LoanRate.DataAccess; +using LCU.API.Internal.LoanRate.Services.MicroApiServices; +using LCU.API.Internal.LoanRateLib.Configurations; +using LCU.Library.Internal.LcuClassPatterns.Crud; +using LCU.Library.Internal.LcuEvaluation; + +string MyAllowSpecificOrigins = "_myAllowSpecificOrigins"; + +var builder = BuildBuilder(args); +BuildServicesAndConfigurations(builder); +BuildAppAndRun(builder); + +WebApplicationBuilder BuildBuilder(string[] args) +{ + var builder = WebApplication.CreateBuilder(args); + builder.Configuration.AddJsonFile("/app/config/environment-appsettings/appsettings.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/app/config/mapped-appsettings/appsettings.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/vault/secrets/loan-rate-queue-password.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/vault/secrets/loan-rate-queue-username.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/vault/secrets/loan-rate-evaluation-connection-string.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/vault/secrets/loan-rate-load-test-connection-string.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/vault/secrets/loan-rate-loan-rate-connection-string.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile("/vault/secrets/loan-rate-seq-api-key.json", optional: true, reloadOnChange: true); + builder.Host.UseSerilog(); + return builder; +} + +void BuildServicesAndConfigurations(WebApplicationBuilder builder) +{ + var services = builder.Services; + var configuration = builder.Configuration; + + var apiConfiguration = configuration.Bind(services, "Primary"); + configuration.Bind(services); + configuration.Bind(services); + configuration.Bind(services); + + var connectionConfiguration = MassTransitConfigurationSetUp.AddConnectionConfiguration(ref services, configuration); + //debugging + Console.WriteLine(JsonConvert.SerializeObject(connectionConfiguration)); + + var receiveEndpointConfigurations = new ReceiveEndpointConfigurations() + { + LoadTestInitiateReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, + "LoadTestInitiateReceiveEndpoint"), + LoadTestProcessRateReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, + "LoadTestProcessRateReceiveEndpoint"), + LoadTestRateCompletionReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration, + "LoadTestRateCompletionReceiveEndpoint") + }; + services.AddSingleton(receiveEndpointConfigurations); + //debugging + Console.WriteLine(JsonConvert.SerializeObject(receiveEndpointConfigurations)); + + var loggerConfiguration = new LoggingConfiguration(apiConfiguration.AppName, + apiConfiguration.SeqApiKey, + apiConfiguration.SeqUrl); + var lcuLogging = new LcuLogging(loggerConfiguration); + lcuLogging.UseLcuLogging(); + + var loanRateSqlConnectionConfiguration = configuration.Bind(services); + services.AddDbContext(options => + { + options.UseSqlServer + ( + loanRateSqlConnectionConfiguration.ConnectionString, + sql => sql.EnableRetryOnFailure(loanRateSqlConnectionConfiguration.MaxRetryCount, + loanRateSqlConnectionConfiguration.MaxRetryDelay, + null) + ); + }); + + var loanRateLoadTestingSqlConnectionConfiguration = configuration.Bind(services); + services.AddDbContext(options => + { + options.UseSqlServer + ( + loanRateLoadTestingSqlConnectionConfiguration.ConnectionString, + sql => sql.EnableRetryOnFailure(loanRateLoadTestingSqlConnectionConfiguration.MaxRetryCount, + loanRateLoadTestingSqlConnectionConfiguration.MaxRetryDelay, + null) + ); + }); + + services.AddCors(options => + { + options.AddPolicy(MyAllowSpecificOrigins, + builder => + { + builder.WithOrigins( + apiConfiguration.CorsAllowedOrigins + ).AllowAnyHeader().AllowAnyMethod(); + }); + }); + + AddMassTransitServices(ref services, connectionConfiguration, receiveEndpointConfigurations, apiConfiguration); + services.AddLCUHttpServices(); + AddLoanRateLib(services); + AddLoanRateLoadTestingLib(services); + AddDataAccess(services); + AddApiServices(services); + + services.AddLcuEvaluationServices(); + services.AddLcuEvaluationCrudServices(configuration); + services.AddLCUStatusServicesWithApiHealthCheck(configuration); + services.AddLcuCrudServices(); + services.AddHttpClient(); + services.AddMemoryCache(); + + services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); + services.AddEndpointsApiExplorer(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = $"{apiConfiguration.AppName}", Version = "v1" }); + }); +} + +void AddMassTransitServices(ref IServiceCollection services, + ConnectionConfiguration connectionConfiguration, + ReceiveEndpointConfigurations receiveEndpointConfigurations, + ApiConfiguration apiConfiguration) +{ + services.AddMassTransit(x => + { + x.AddConsumer(); + x.AddConsumer(); + x.AddConsumer(); + + x.UsingRabbitMq((context, cfg) => + { + cfg.Host(connectionConfiguration.HostUrl, "/", host => + { + host.Username(connectionConfiguration.UserName); + host.Password(connectionConfiguration.Password); + }); + cfg.UseTimeout(t => t.Timeout = TimeSpan.FromSeconds(420)); + + if (apiConfiguration.FeatureFlags.RunLoadTestInitiateConsumer) + { + cfg.ReceiveEndpoint(receiveEndpointConfigurations.LoadTestInitiateReceiveEndpoint.ReceiveEndpoint, e => + { + e.ConfigureConsumer(context); + e.SetQuorumQueue(); + e.PrefetchCount = receiveEndpointConfigurations.LoadTestInitiateReceiveEndpoint.PrefetchLimit; + e.UseTimeout(t => t.Timeout = TimeSpan.FromSeconds(420)); + }); + } + + if (apiConfiguration.FeatureFlags.RunLoadTestProcessRateConsumer) + { + cfg.ReceiveEndpoint(receiveEndpointConfigurations.LoadTestProcessRateReceiveEndpoint.ReceiveEndpoint, e => + { + e.ConfigureConsumer(context); + e.SetQuorumQueue(); + e.PrefetchCount = receiveEndpointConfigurations.LoadTestProcessRateReceiveEndpoint.PrefetchLimit; + e.UseTimeout(t => t.Timeout = TimeSpan.FromSeconds(420)); + }); + } + + if (apiConfiguration.FeatureFlags.RunLoadTestRateCompletionConsumer) + { + cfg.ReceiveEndpoint(receiveEndpointConfigurations.LoadTestRateCompletionReceiveEndpoint.ReceiveEndpoint, e => + { + e.ConfigureConsumer(context, cons => + { + //TODO: Will need to add optional configuration in our LCU Masstransit Library for these configurables. + cons.Options(options => options + .SetMessageLimit(100) + .SetTimeLimit(5000) + .SetConcurrencyLimit(1)); + }); + e.SetQuorumQueue(); + e.PrefetchCount = receiveEndpointConfigurations.LoadTestRateCompletionReceiveEndpoint.PrefetchLimit; + e.UseTimeout(t => t.Timeout = TimeSpan.FromSeconds(420)); + }); + } + }); + }); + services.AddMassTransitHostedService(); +} + + +void AddApiServices(IServiceCollection services) +{ + services.AddScoped(); + + AddLoadTestingServices(services); + AddLoanRateOperationServices(services); + AddValidateLoanRateServices(services); + AddMicroApiServices(services); + AddQueueServices(services); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddLoadTestingServices(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddLoanRateOperationServices(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddValidateLoanRateServices(IServiceCollection services) +{ + services.AddScoped(); +} + +void AddMicroApiServices(IServiceCollection services) +{ + services.AddScoped(); +} + +void AddQueueServices(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddDataAccess(IServiceCollection services) +{ + services.AddScoped(); +} + +void AddLoanRateLib(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void AddLoanRateLoadTestingLib(IServiceCollection services) +{ + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); +} + +void BuildAppAndRun(WebApplicationBuilder builder) +{ + var app = builder.Build(); + + // Configure the HTTP request pipeline. + if (app.Environment.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseSwagger(c => + { + c.PreSerializeFilters.Add((swaggerDoc, request) => + { + const string prefixHeader = "X-Forwarded-Prefix"; + if (!request.Headers.ContainsKey(prefixHeader)) return; + + var serverUrl = request.Headers[prefixHeader]; + if (!string.IsNullOrWhiteSpace(serverUrl)) + { + swaggerDoc.Servers = new List + { + new() { Description = serverUrl, Url = serverUrl } + }; + } + }); + }); + app.UseSwaggerUI(c => + { + c.RoutePrefix = "swagger"; + c.SwaggerEndpoint("v1/swagger.json", "LCU.API.Internal.LoanRate v1"); + }); + app.UseMiddleware(); + app.UseSerilogRequestLogging(options => + { + options.EnrichDiagnosticContext = RequestResponseLoggingMiddleware.RequestDataParser.EnrichFromRequest; + }); + + app.UseRouting(); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseCors(MyAllowSpecificOrigins); + app.UseHttpsRedirection(); + app.MapControllers(); + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + + app.Run(); +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Properties/launchSettings.json b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Properties/launchSettings.json new file mode 100644 index 0000000..47c72b6 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:28863", + "sslPort": 44350 + } + }, + "profiles": { + "API": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7068;http://localhost:5159", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ComplexEvaluationDetailService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ComplexEvaluationDetailService.cs new file mode 100644 index 0000000..2c43d10 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ComplexEvaluationDetailService.cs @@ -0,0 +1,90 @@ +using LCU.Library.Internal.LcuEvaluation.Services; +using LCU.Library.Internal.LcuEvaluationModels.Entities; +using LCU.Library.Internal.LcuEvaluationModels.Requests; +using Newtonsoft.Json; + +namespace LCU.API.Internal.LoanRate.Services; + +public class ComplexEvaluationDetailService : IComplexEvaluationDetailService +{ + private readonly IRateEvaluationService _rateEvaluationService; + private readonly IEvaluationDetailService _evaluationDetailService; + + public ComplexEvaluationDetailService(IRateEvaluationService rateEvaluationService, + IEvaluationDetailService evaluationDetailService) + { + _rateEvaluationService = rateEvaluationService; + _evaluationDetailService = evaluationDetailService; + } + + public async Task UpdateComplexAsync(EvaluationDetail evaluationDetail) + { + try + { + return await _evaluationDetailService.UpdateComplexAsync(evaluationDetail); + } + catch (Exception exception) + { + var message = $"Failure completing complex update for EvaluationDetail. EvaluationDetailId: {evaluationDetail.EvaluationDetailId}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task SaveComplexAsync(EvaluationDetailRequest request) + { + try + { + var newEvaluationDetail = await _evaluationDetailService.SaveComplexAsync(request); + + var previousVersionId = request.PreviousVersionEvaluationDetailId ?? 0; + if (previousVersionId <= 0) + { + return newEvaluationDetail; + } + + var rateEvaluations = + (await _rateEvaluationService.GetRangeByEvaluationDetailIdAsync(previousVersionId)).ToList(); + + if (rateEvaluations is null || !rateEvaluations.Any()) + { + return newEvaluationDetail; + } + + var newRateEvaluations = rateEvaluations.Select(rateEvaluation => + new RateEvaluation + { + RateId = rateEvaluation.RateId, + EvaluationDetailId = newEvaluationDetail.EvaluationDetailId + }).ToList(); + + await _rateEvaluationService.SaveRangeAsync(newRateEvaluations); + + return newEvaluationDetail; + + } + catch (Exception exception) + { + var message = "Failure on SaveComplexAsync. " + + $"EvaluationDetailRequest: ({JsonConvert.SerializeObject(request)})"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task DeleteComplexAsync(int evaluationDetailId) + { + try + { + await _rateEvaluationService.DeleteRangeByEvaluationDetailIdAsync(evaluationDetailId); + await _evaluationDetailService.DeleteComplexAsync(evaluationDetailId); + } + catch (Exception exception) + { + var message = "Failure on DeleteComplexAsync. " + + $"EvaluationDetailId: ({evaluationDetailId})"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IComplexEvaluationDetailService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IComplexEvaluationDetailService.cs new file mode 100644 index 0000000..40540cb --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IComplexEvaluationDetailService.cs @@ -0,0 +1,11 @@ +using LCU.Library.Internal.LcuEvaluationModels.Entities; +using LCU.Library.Internal.LcuEvaluationModels.Requests; + +namespace LCU.API.Internal.LoanRate.Services; + +public interface IComplexEvaluationDetailService +{ + Task UpdateComplexAsync(EvaluationDetail evaluationDetail); + Task SaveComplexAsync(EvaluationDetailRequest request); + Task DeleteComplexAsync(int evaluationDetailId); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ILoadTestRunInfoService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ILoadTestRunInfoService.cs new file mode 100644 index 0000000..69f8295 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ILoadTestRunInfoService.cs @@ -0,0 +1,19 @@ +using LCU.Library.Internal.LCUFilterModels.Pagination; +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; + +namespace LCU.API.Internal.LoanRate.Services; + +public interface ILoadTestRunInfoService +{ + Task> GetAsync(); + Task GetByIdAsync(int id); + Task> GetRangeByIdAsync(List ids); + Task> GetFilteredLoadTestRunInfo(LoadTestRunInfoFilter loadTestRunInfoFilter); + Task SaveAsync(LoadTestRunInfo loadTestRunInfo); + Task SaveRangeAsync(IEnumerable loadTestRunInfos); + Task IncreaseRateProcessingInfosCompleteCountAsync(int loadTestRunInfoId, int countCompleted); + Task UpdateAsync(LoadTestRunInfo loadTestRunInfo); + Task> UpdateRangeAsync(IEnumerable loadTestRunInfos); + Task DeleteAsync(int id); + Task DeleteRangeAsync(List ids); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateEvaluationService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateEvaluationService.cs new file mode 100644 index 0000000..93dd665 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateEvaluationService.cs @@ -0,0 +1,20 @@ +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.API.Internal.LoanRate.Services; + +public interface IRateEvaluationService +{ + Task> GetAsync(); + Task GetByIdAsync(int id); + Task> GetRangeByIdAsync(List ids); + Task> GetRangeByEvaluationDetailIdAsync(int evaluationDetailId); + Task> GetByRateSheetIdsAsync(List rateSheetIds); + Task SaveAsync(RateEvaluation rateEvaluation); + Task SaveRangeAsync(IEnumerable rateEvaluations); + Task UpdateAsync(RateEvaluation rateEvaluation); + Task> UpdateRangeAsync(IEnumerable rateEvaluations); + Task DeleteAsync(int id); + Task DeleteRangeAsync(List ids); + Task DeleteRangeByEvaluationDetailIdAsync(int evaluationDetailId); + Task CountByEvaluationDetailIdAsync(int evaluationDetailId); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateProcessingInfoService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateProcessingInfoService.cs new file mode 100644 index 0000000..2798828 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateProcessingInfoService.cs @@ -0,0 +1,16 @@ +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +namespace LCU.API.Internal.LoanRate.Services; + +public interface IRateProcessingInfoService +{ + Task> GetAsync(); + Task GetByIdAsync(int id); + Task> GetRangeByIdAsync(List ids); + Task?> GetByLoadTestRunInfoId(int loadTestRunInfoId); + Task SaveAsync(RateProcessingInfo rateProcessingInfo); + Task SaveRangeAsync(IEnumerable rateProcessingInfos); + Task UpdateAsync(RateProcessingInfo rateProcessingInfo); + Task> UpdateRangeAsync(IEnumerable rateProcessingInfos); + Task DeleteAsync(int id); + Task DeleteRangeAsync(List ids); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateService.cs new file mode 100644 index 0000000..c271a0f --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateService.cs @@ -0,0 +1,23 @@ +using LCU.Library.Internal.LcuEvaluationModels.Enums; + +namespace LCU.API.Internal.LoanRate.Services; +public interface IRateService +{ + Task> GetAsync(); + Task GetByIdAsync(int id); + Task?> GetByClosingRateUploadInfoIdAsync(int id); + Task?> GetByDateAsync(DateTime date); + Task?> GetRateHistory(Rate rate); + Task> GetRangeByIdAsync(List ids); + Task?> GetByDateEvaluationIdAndEvaluationTypeAsync(DateTime date, + List evaluationIds, + EvaluationTypes evaluationType); + Task>?> GetByLoadTestRunInfoId(int loadTestRunInfoId); + Task SaveAsync(Rate rate); + Task SaveRangeAsync(IEnumerable rates); + Task UpdateAsync(Rate rate); + Task> UpdateRangeAsync(IEnumerable rates); + Task DeleteAsync(int id); + Task DeleteRangeAsync(List ids); + void ClearCache(); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateSheetCellService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateSheetCellService.cs new file mode 100644 index 0000000..b38843e --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateSheetCellService.cs @@ -0,0 +1,6 @@ +using LCU.Library.Internal.LcuClassPatterns.Crud.Services; + +namespace LCU.API.Internal.LoanRate.Services; +public interface IRateSheetCellService : ICrudService +{ +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateSheetService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateSheetService.cs new file mode 100644 index 0000000..2b7f91d --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateSheetService.cs @@ -0,0 +1,15 @@ +using LCU.Library.Internal.LcuClassPatterns.Crud.Services; +using LCU.Library.Internal.LoanRateModels.Enums; +using LCU.Library.Internal.LoanRateModels.Responses; + +namespace LCU.API.Internal.LoanRate.Services; +public interface IRateSheetService : ICrudService +{ + Task?> GetIncludeAllAsync(); + Task?> GetIncludeRateSheetCellsAsync(); + Task?> GetRatesByDateAndRateTypeAsync(DateTime date, RateTypes rateType); + Task GetByDateAndRateTypeAsync(DateTime date, RateTypes rateType); + Task?> GetRatesByRateTypeAsync(RateTypes rateType); + Task?> GetRateSheetIdsByRateTypeAsync(RateTypes rateType); + void ClearCache(); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateUploadInfoService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateUploadInfoService.cs new file mode 100644 index 0000000..9124309 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IRateUploadInfoService.cs @@ -0,0 +1,14 @@ +using LCU.Library.Internal.LcuClassPatterns.Crud.Services; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LoanRateModels.Responses; + +namespace LCU.API.Internal.LoanRate.Services; +public interface IRateUploadInfoService : ICrudService +{ + Task GetLatestAsync(); + Task GetByVersionAsync(int version, DateTime effectiveDate); + Task GetLatestVersionOnDate(DateTime effectiveDate); + Task GetFilteredRateUploadInfo(RateUploadInfoFilter rateUploadInfoFilter); + Task ComplexDelete(int id); + Task ComplexSave(RateUploadInfoRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IStatusService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IStatusService.cs new file mode 100644 index 0000000..3f8cebe --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/IStatusService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.StatusModels.Models; +namespace LCU.API.Internal.LoanRate.Services; + +public interface IStatusService +{ + EnvironmentView GetEnviromentView(); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestRunInfoService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestRunInfoService.cs new file mode 100644 index 0000000..554ae9f --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestRunInfoService.cs @@ -0,0 +1,260 @@ +using Microsoft.Data.SqlClient; +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +using LCU.API.Internal.LoanRateLoadTestingLib.Repositories; +using LCU.Library.Internal.LCUFilterModels.Pagination; + +namespace LCU.API.Internal.LoanRate.Services; + +public class LoadTestRunInfoService : ILoadTestRunInfoService +{ + private readonly IUnitOfWork _unitOfWork; + private readonly ILoadTestRunInfoRepository _loadTestRunInfoRepository; + + public LoadTestRunInfoService(IUnitOfWork unitOfWork, ILoadTestRunInfoRepository loadTestRunInfoRepository) + { + _unitOfWork = unitOfWork; + _loadTestRunInfoRepository = loadTestRunInfoRepository; + } + + public async Task> GetAsync() + { + try + { + return await _loadTestRunInfoRepository.FindAsync() ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving LoadTestRunInfos."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task GetByIdAsync(int id) + { + try + { + return await _loadTestRunInfoRepository.FindByIdAsync(id); + } + catch (Exception exception) + { + var message = $"Failure retrieving LoadTestRunInfo with id: {id}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task> GetRangeByIdAsync(List ids) + { + try + { + return await _loadTestRunInfoRepository.FindRangeByIdAsync(ids) ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving LoadTestRunInfos. " + + $"Ids: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + + public async Task> GetFilteredLoadTestRunInfo(LoadTestRunInfoFilter loadTestRunInfoFilter) + { + try + { + return await _loadTestRunInfoRepository.FindFilteredLoadTestRunInfosAsync(loadTestRunInfoFilter); + } + catch (Exception exception) + { + var message = "Failure retrieving filtered LoadTestRunInfos."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task SaveAsync(LoadTestRunInfo loadTestRunInfo) + { + try + { + var result = await _loadTestRunInfoRepository.AddAsync(loadTestRunInfo); + + if (result is null) + { + throw new Exception("Saving a LoadTestRunInfo returned null."); + } + + await _unitOfWork.CompleteAsync(); + return result; + } + catch (Exception exception) + { + var message = "Failure saving LoadTestRunInfo."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task SaveRangeAsync(IEnumerable loadTestRunInfos) + { + try + { + await _loadTestRunInfoRepository.AddRangeAsync(loadTestRunInfos); + await _unitOfWork.CompleteAsync(); + } + catch (Exception exception) + { + var message = "Failure saving list of LoadTestRunInfos."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task IncreaseRateProcessingInfosCompleteCountAsync(int loadTestRunInfoId, int countCompleted) + { + try + { + var existing = await GetByIdAsync(loadTestRunInfoId); + if (existing is null) + { + Log.Error($"LoadTestRunInfo with id:{loadTestRunInfoId} was not found."); + return null; + } + + existing.RateProcessingInfosCompleteCount = existing.RateProcessingInfosCompleteCount + countCompleted; + + if (existing.RateProcessingInfosCount == existing.RateProcessingInfosCompleteCount) + { + existing.EndTimeStamp = DateTime.Now; + } + + await _unitOfWork.CompleteAsync(); + return existing; + } + catch (SqlException sqlException) + { + var message = $"SQL Failure LoadTestRunInfo Increasing Rate Processing Infos Complete Count. LoadTestRunInfoId: {loadTestRunInfoId}"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = $"Failure LoadTestRunInfo Increasing Rate Processing Infos Complete Count. LoadTestRunInfoId: {loadTestRunInfoId}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task UpdateAsync(LoadTestRunInfo loadTestRunInfo) + { + try + { + var existing = await GetByIdAsync(loadTestRunInfo.LoadTestRunInfoId); + if (existing is null) + { + Log.Error($"LoadTestRunInfo with id:{loadTestRunInfo.LoadTestRunInfoId} was not found."); + return null; + } + + existing.StartTimeStamp = loadTestRunInfo.StartTimeStamp; + existing.EndTimeStamp = loadTestRunInfo.EndTimeStamp; + existing.RateType = loadTestRunInfo.RateType; + existing.RateProcessingInfosCount = loadTestRunInfo.RateProcessingInfosCount; + existing.RateProcessingInfosCompleteCount = loadTestRunInfo.RateProcessingInfosCompleteCount; + + await _unitOfWork.CompleteAsync(); + return existing; + } + catch (SqlException sqlException) + { + var message = $"SQL Failure updating LoadTestRunInfo. LoadTestRunInfoId: {loadTestRunInfo.LoadTestRunInfoId}"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = $"Failure updating LoadTestRunInfo. LoadTestRunInfoId: {loadTestRunInfo.LoadTestRunInfoId}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task> UpdateRangeAsync(IEnumerable loadTestRunInfos) + { + try + { + var records = loadTestRunInfos.ToList(); + + _loadTestRunInfoRepository.UpdateRange(records); + await _unitOfWork.CompleteAsync(); + + return records; + } + catch (SqlException sqlException) + { + var message = "SQL Failure updating list of LoadTestRunInfos."; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = "Failure updating list of LoadTestRunInfos."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task DeleteAsync(int id) + { + try + { + var loadTestRunInfo = await GetByIdAsync(id); + ArgumentNullException.ThrowIfNull(loadTestRunInfo); + _loadTestRunInfoRepository.Remove(loadTestRunInfo); + await _unitOfWork.CompleteAsync(); + } + catch (SqlException sqlException) + { + var message = $"SQL failure removing LoadTestRunInfo. LoadTestRunInfoId: {id}"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = $"Failure removing LoadTestRunInfo. LoadTestRunInfoId: {id}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task DeleteRangeAsync(List ids) + { + try + { + var loadTestRunInfos = await GetRangeByIdAsync(ids); + + if (loadTestRunInfos is null || !loadTestRunInfos.Any()) + { + throw new Exception("GetRangeByIdAsync returned no LoadTestRunInfos to remove."); + } + + _loadTestRunInfoRepository.RemoveRange(loadTestRunInfos); + await _unitOfWork.CompleteAsync(); + } + catch (SqlException sqlException) + { + var message = "SQL failure removing list of LoadTestRunInfos. " + + $"LoadTestRunInfoIds: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = "Failure removing list of LoadTestRunInfos. " + + $"LoadTestRunInfoIds: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/ILoadTestInitiationService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/ILoadTestInitiationService.cs new file mode 100644 index 0000000..a38b5de --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/ILoadTestInitiationService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +namespace LCU.API.Internal.LoanRate.Services.LoadTestingServices; + +public interface ILoadTestInitiationService +{ + Task HandleLoadTestInitiation(LoadTestInitiateRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/ILoadTestProcessRateService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/ILoadTestProcessRateService.cs new file mode 100644 index 0000000..e941177 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/ILoadTestProcessRateService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +namespace LCU.API.Internal.LoanRate.Services.LoadTestingServices; + +public interface ILoadTestProcessRateService +{ + Task HandleLoadTestProcessRate(LoadTestProcessRateRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/ILoadTestRateCompletionService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/ILoadTestRateCompletionService.cs new file mode 100644 index 0000000..91f2d91 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/ILoadTestRateCompletionService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +namespace LCU.API.Internal.LoanRate.Services.LoadTestingServices; + +public interface ILoadTestRateCompletionService +{ + Task HandleLoadTestRateCompletion(List requests); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/LoadTestInitiationService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/LoadTestInitiationService.cs new file mode 100644 index 0000000..33eb874 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/LoadTestInitiationService.cs @@ -0,0 +1,180 @@ +using Newtonsoft.Json; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.API.Internal.LoanRate.Services.QueueServices; +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +using LCU.Library.Internal.LoanRateModels.Enums; +using LCU.Library.Internal.LoanRateModels.Enums.LoadTestingEnums; + +namespace LCU.API.Internal.LoanRate.Services.LoadTestingServices; + +public class LoadTestInitiationService : ILoadTestInitiationService +{ + private readonly ILoadTestRunInfoService _loadTestRunInfoService; + private readonly IRateProcessingInfoService _rateProcessingInfoService; + private readonly IQueueLoadTestProcessRateService _queueLoadTestProcessRateService; + + public LoadTestInitiationService(ILoadTestRunInfoService loadTestRunInfoService, + IRateProcessingInfoService rateProcessingInfoService, + IQueueLoadTestProcessRateService queueLoadTestProcessRateService) + { + _loadTestRunInfoService = loadTestRunInfoService; + _rateProcessingInfoService = rateProcessingInfoService; + _queueLoadTestProcessRateService = queueLoadTestProcessRateService; + } + + public async Task HandleLoadTestInitiation(LoadTestInitiateRequest request) + { + try + { + var combinations = GenerateModelCombinations(request.PropertyList); + + var loadTestRunInfo = new LoadTestRunInfo + { + StartTimeStamp = DateTime.Now, + RateType = (RateTypes)request.RateType, + RateProcessingInfosCount = combinations.Count + }; + + combinations = SetRateType(request.RateType, combinations); + + loadTestRunInfo = await _loadTestRunInfoService.SaveAsync(loadTestRunInfo); + var rateProcessingInfos = await CreateAndSaveCombinationsRateProcessingInfos(combinations, loadTestRunInfo.LoadTestRunInfoId); + await CreateLoadTestProcessRateRequestAndQueue(rateProcessingInfos); + } + catch (Exception exception) + { + var message = $"Failure during Handle Load Test initiation. Exception Message: {exception.Message}"; + Log.Error(message); + throw new ServiceException(message); + } + } + + private async Task> CreateAndSaveCombinationsRateProcessingInfos(List combinations, int loadTestRunInfoId) + { + var rateProcessingInfos = new List(); + foreach (var combination in combinations) + { + var rateProcessingInfo = new RateProcessingInfo + { + LoadTestRunInfoId = loadTestRunInfoId, + Payload = JsonConvert.SerializeObject(combination), + RateProcessingStatusType = RateProcessingStatusTypes.Processing + }; + + rateProcessingInfos.Add(rateProcessingInfo); + } + await _rateProcessingInfoService.SaveRangeAsync(rateProcessingInfos); + var newRateProcessingInfos = await _rateProcessingInfoService.GetByLoadTestRunInfoId(loadTestRunInfoId); + return newRateProcessingInfos.ToList(); + } + + private async Task CreateLoadTestProcessRateRequestAndQueue(List rateProcessingInfos) + { + var tasks = new List(); + foreach (var rateProcessingInfo in rateProcessingInfos) + { + var loadTestProcessRateRequest = new LoadTestProcessRateRequest + { + RateProcessingInfo = rateProcessingInfo + }; + tasks.Add(_queueLoadTestProcessRateService.QueueLoadTestProcessRateRequest(loadTestProcessRateRequest)); + } + + await Task.WhenAll(tasks); + } + + private static List SetRateType(RateTypes? rateType, List combinations) + { + foreach (var combination in combinations) + { + combination.RateType = (RateTypes)rateType; + } + + return combinations; + } + + private List GenerateModelCombinations(Dictionary> propertyLists) + { + var combinations = new List(); + GenerateModelCombinationsHelper(propertyLists, combinations, Activator.CreateInstance(), propertyLists.Keys.ToArray(), 0); + return combinations; + } + + private void GenerateModelCombinationsHelper(IReadOnlyDictionary> propertyLists, + ICollection combinations, + TModel currentModel, + IReadOnlyList propertyNames, + int currentIndex) + { + if (currentIndex == propertyNames.Count) + { + combinations.Add(CreateNewInstanceOfModel(currentModel)); + return; + } + + var propertyName = propertyNames[currentIndex]; + foreach (var item in propertyLists[propertyName]) + { + currentModel = SetModelValueByPropertyName(currentModel, item, propertyName); + GenerateModelCombinationsHelper(propertyLists, combinations, currentModel, propertyNames, currentIndex + 1); + } + } + + private TModel CreateNewInstanceOfModel(TModel originalObject) + { + var newInstance = Activator.CreateInstance(); + var properties = typeof(TModel).GetProperties(); + foreach (var property in properties) + { + var value = property.GetValue(originalObject); + property.SetValue(newInstance, value); + } + + return newInstance; + } + + private TModel SetModelValueByPropertyName(TModel currentModel, string item, string propertyName) + { + var targetTypes = GetObjectsPropertyTypes(currentModel); + foreach (var type in targetTypes) + { + if (currentModel.GetType().GetProperty(propertyName)?.PropertyType.FullName == type.FullName) + { + var finalItem = ConvertPropertyStringValueToType(item, type); + currentModel.GetType().GetProperty(propertyName)?.SetValue(currentModel, finalItem); + return currentModel; + } + } + + return currentModel; + } + + private static object ConvertPropertyStringValueToType(string value, Type type) + { + if (type.IsEnum) + { + return Enum.Parse(type, value); + } + + return Convert.ChangeType(value, type); + } + + static List GetObjectsPropertyTypes(object obj) + { + var objectType = obj.GetType(); + var propertyTypes = new List(); + var properties = objectType.GetProperties(); + + foreach (var propertyInfo in properties) + { + if (!propertyTypes.Contains(propertyInfo.PropertyType)) + { + propertyTypes.Add(propertyInfo.PropertyType); + } + } + + return propertyTypes; + } + +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/LoadTestProcessRateService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/LoadTestProcessRateService.cs new file mode 100644 index 0000000..6ce5cfb --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/LoadTestProcessRateService.cs @@ -0,0 +1,78 @@ +using Newtonsoft.Json; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +using LCU.API.Internal.LoanRate.Services.QueueServices; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +using LCU.Library.Internal.LoanRateModels.Enums.LoadTestingEnums; +namespace LCU.API.Internal.LoanRate.Services.LoadTestingServices; + +public class LoadTestProcessRateService : ILoadTestProcessRateService +{ + private readonly IRateFinderService _rateFinderService; + private readonly IQueueLoadTestRateCompletionService _queueLoadTestRateCompletionService; + + public LoadTestProcessRateService(IRateFinderService rateFinderService, + IQueueLoadTestRateCompletionService queueLoadTestRateCompletionService) + { + _rateFinderService = rateFinderService; + _queueLoadTestRateCompletionService = queueLoadTestRateCompletionService; + } + + public async Task HandleLoadTestProcessRate(LoadTestProcessRateRequest request) + { + try + { + var rateRequest = JsonConvert.DeserializeObject(request.RateProcessingInfo.Payload); + var rateProcessingInfo = await FindRateDetails(request.RateProcessingInfo, rateRequest); + + rateProcessingInfo.Rate = rateProcessingInfo.Rate > 0 ? rateProcessingInfo.Rate : (decimal)99.0; + var loadTestRateCompletionRequest = new LoadTestRateCompletionRequest + { + RateProcessingInfo = rateProcessingInfo + }; + + await _queueLoadTestRateCompletionService.QueueLoadTestRateCompletionRequest(loadTestRateCompletionRequest); + } + catch (Exception exception) + { + var message = $"Failure during Handle Load Test Process Rate. Exception Message: {exception.Message}"; + Log.Error(message); + throw new ServiceException(message); + } + } + + private async Task FindRateDetails(RateProcessingInfo rateProcessingInfo, + TRateRequest rateRequest) + where TRateRequest : RateRequest + { + try + { + var passedRules = await _rateFinderService.DeterminePassedRules(rateRequest); + var rateResponse = await _rateFinderService.DetermineRate(rateRequest.ApplicationDate, passedRules); + + if (rateResponse.rate is null) + { + throw new ServiceException(rateResponse.message); + } + + var rateValue = await _rateFinderService.CalculateFinalRateValue(rateRequest, rateResponse.rate); + + rateProcessingInfo.Column = rateResponse.rate.Column; + rateProcessingInfo.Row = rateResponse.rate.Row; + rateProcessingInfo.Rate = rateValue; + rateProcessingInfo.RateProcessingResultType = RateProcessingResultTypes.RateFound; + } + catch (Exception exception) + { + rateProcessingInfo.ProcessingMessage = exception.Message; + rateProcessingInfo.RateProcessingResultType = RateProcessingResultTypes.RateFailure; + } + finally + { + rateProcessingInfo.RateProcessingStatusType = RateProcessingStatusTypes.Complete; + } + + return rateProcessingInfo; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/LoadTestRateCompletionService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/LoadTestRateCompletionService.cs new file mode 100644 index 0000000..d345aec --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoadTestingServices/LoadTestRateCompletionService.cs @@ -0,0 +1,39 @@ +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +namespace LCU.API.Internal.LoanRate.Services.LoadTestingServices; + +public class LoadTestRateCompletionService : ILoadTestRateCompletionService +{ + private readonly IRateProcessingInfoService _rateProcessingInfoService; + private readonly ILoadTestRunInfoService _loadTestRunInfoService; + + public LoadTestRateCompletionService(IRateProcessingInfoService rateProcessingInfoService, ILoadTestRunInfoService loadTestRunInfoService) + { + _rateProcessingInfoService = rateProcessingInfoService; + _loadTestRunInfoService = loadTestRunInfoService; + } + + public async Task HandleLoadTestRateCompletion(List requests) + { + try + { + var rateProcessingInfos = requests.Select(request => request.RateProcessingInfo).ToList(); + await _rateProcessingInfoService.UpdateRangeAsync(rateProcessingInfos); + + var loadTestRunInfoGroups = rateProcessingInfos.GroupBy( + p => p.LoadTestRunInfoId, + p => p.RateProcessingInfoId, + (key, g) => new { LoadTestRunInfoId = key, RateProcessingInfoIds = g.ToList() }); + foreach(var loadTestRunInfoGroup in loadTestRunInfoGroups) + { + await _loadTestRunInfoService.IncreaseRateProcessingInfosCompleteCountAsync(loadTestRunInfoGroup.LoadTestRunInfoId, + loadTestRunInfoGroup.RateProcessingInfoIds.Count); + } + } + catch (Exception exception) + { + var message = $"Failure during Handle Load Test Rate Completion. Exception Message: {exception.Message}"; + Log.Error(message); + throw new ServiceException(message); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateFinderService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateFinderService.cs new file mode 100644 index 0000000..0fda0e2 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateFinderService.cs @@ -0,0 +1,14 @@ +using LCU.Library.Internal.LcuEvaluationModels.Entities; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LoanRateModels.Responses; + +namespace LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; + +public interface IRateFinderService +{ + Task FindRate(TRequest request) where TRequest : RateRequest; + Task FindRateObject(TRequest request) where TRequest : RateRequest; + Task> DeterminePassedRules(TRequest request) where TRequest : RateRequest; + Task<(Rate rate, string message)> DetermineRate(DateTime applicationDate, List passedRules); + Task CalculateFinalRateValue(TRequest request, Rate rate) where TRequest : RateRequest; +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateSheetCellUploaderService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateSheetCellUploaderService.cs new file mode 100644 index 0000000..b4cc6b9 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateSheetCellUploaderService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LoanRateModels.Requests; + +namespace LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +public interface IRateSheetCellUploaderService +{ + public Task> UploadRateSheetCellsAsync(RateSheetCellUploadRequest request); +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateUploaderService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateUploaderService.cs new file mode 100644 index 0000000..46a9146 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateUploaderService.cs @@ -0,0 +1,12 @@ +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Requests; +using LCU.Library.Internal.LoanRateModels.Responses; + +namespace LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; + +public interface IRateUploaderService +{ + Task UploadRates(RateUploadRequest request); + Task UploadRates(RateUploadExcelDataRequest request); + Task> InitiateRatesEvaluations(IEnumerable requests); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateValidationService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateValidationService.cs new file mode 100644 index 0000000..7d3a2f8 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/IRateValidationService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LoanRateModels.Responses.Validation.RateSheetValidations; + +namespace LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +public interface IRateValidationService +{ + Task> ValidateAsync(List rates); +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateFinderService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateFinderService.cs new file mode 100644 index 0000000..d437510 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateFinderService.cs @@ -0,0 +1,129 @@ +using LCU.Library.Internal.LcuEvaluationModels.Enums; +using LCU.Library.Internal.LcuEvaluationModels.Requests; +using LCU.Library.Internal.LcuEvaluation.Services; +using LCU.Library.Internal.LcuEvaluation.Services.EvaluationServices; +using LCU.Library.Internal.LcuEvaluationModels.Entities; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LoanRateModels.Responses; + +namespace LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; + +public class RateFinderService : IRateFinderService +{ + private readonly IEvaluationDetailService _evaluationDetailService; + private readonly IRateService _rateService; + private readonly IRateSheetService _rateSheetService; + private readonly IRateEvaluationService _rateEvaluationService; + private readonly IRunRuleService _runRuleService; + private readonly IRunCalculationService _runCalculationService; + + public RateFinderService(IEvaluationDetailService evaluationDetailService, + IRateService rateService, + IRateSheetService rateSheetService, + IRateEvaluationService rateEvaluationService, + IRunRuleService runRuleService, + IRunCalculationService runCalculationService) + { + _evaluationDetailService = evaluationDetailService; + _rateService = rateService; + _rateSheetService = rateSheetService; + _rateEvaluationService = rateEvaluationService; + _runRuleService = runRuleService; + _runCalculationService = runCalculationService; + } + + public async Task FindRate(TRequest request) where TRequest : RateRequest + { + try + { + var passedRules = await DeterminePassedRules(request); + var rate = await DetermineRate(request.ApplicationDate, passedRules); + return rate.rate is not null + ? new RateResponse { Rate = await CalculateFinalRateValue(request, rate.rate) } + : new RateResponse().SetDefault().AddErrorMessage(rate.message); + } + catch (Exception exception) + { + var message = $"Failure during FindRate. ExceptionMessage: {exception.Message}"; + Log.Error(message, exception); + throw new ServiceException(message); + } + } + + public async Task FindRateObject(TRequest request) where TRequest : RateRequest + { + try + { + var passedRules = await DeterminePassedRules(request); + return (await DetermineRate(request.ApplicationDate, passedRules)).rate; + } + catch (Exception exception) + { + var message = $"Failure during FindRateObject. ExceptionMessage: {exception.Message}"; + Log.Error(message, exception); + throw new ServiceException(message); + } + } + + public async Task> DeterminePassedRules(TRequest request) where TRequest : RateRequest + { + var rateSheetIds = (await _rateSheetService.GetRateSheetIdsByRateTypeAsync(request.RateType))?.ToList(); + + if (rateSheetIds is null || rateSheetIds.Count == 0) + { + throw new ServiceException("No RateSheets were found to help determine rates."); + } + + var rateEvaluations = await _rateEvaluationService.GetByRateSheetIdsAsync(rateSheetIds); + var ratesEvaluationDetailIds = rateEvaluations.GroupBy(rateEvaluation => rateEvaluation.EvaluationDetailId) + .Select(group => group.First().EvaluationDetailId) + .ToList(); + var rules = (await _evaluationDetailService.GetActiveByEvaluationTypeAndIdsAsync(request.ApplicationDate, + EvaluationTypes.Rule, + ratesEvaluationDetailIds))?.ToList(); + if (rules is null || rules.Count == 0) + { + throw new ServiceException("There were no Evaluations to help determine rates."); + } + + var passedRules = _runRuleService.ValidateRequestRules(request, rules.ToList()); + return passedRules; + } + + public async Task<(Rate? rate, string message)> DetermineRate(DateTime applicationDate, List passedRules) + { + var evaluationIds = passedRules.Select(passedRule => passedRule.EvaluationDetailId).ToList(); + var matchingRates = (await _rateService.GetByDateEvaluationIdAndEvaluationTypeAsync(applicationDate, evaluationIds, EvaluationTypes.Rule))?.ToList(); + + if (matchingRates?.FirstOrDefault() == null) + { + return ( null, "There were no Rates that matched the evaluations that have passed."); + } + + if (matchingRates.Count > 1) + { + return ( null, "There were more then one Rate that matched the evaluations that passed. " + + "This should not happen and the evaluations to rates needs to be confirmed."); + } + + return ( matchingRates.FirstOrDefault(), string.Empty ); + } + + public async Task CalculateFinalRateValue(TRequest request, Rate rate) where TRequest : RateRequest + { + var possibleCalculationIds = rate.RateEvaluations.Select(rateEvaluation => rateEvaluation.EvaluationDetailId).ToList(); + var calculations = await _evaluationDetailService.GetActiveByEvaluationTypeAndIdsAsync(request.ApplicationDate, EvaluationTypes.Calculation, possibleCalculationIds); + + foreach (var calculation in calculations) + { + var calculationRequest = new RateCalculationRequest + { + CurrentRate = rate.Value + }; + var requests = new List { request, calculationRequest }; + var calculatedRate = _runCalculationService.CalculateEvaluationWithSetOfRequests(requests, calculation); + rate.Value = calculatedRate == null ? rate.Value : (decimal)calculatedRate; + } + return rate.Value; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateSheetCellUploaderService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateSheetCellUploaderService.cs new file mode 100644 index 0000000..7813b87 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateSheetCellUploaderService.cs @@ -0,0 +1,160 @@ +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Requests; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Responses; +using LCU.API.Internal.LoanRate.Services.MicroApiServices; +using LCU.Library.Internal.LoanRateModels.Enums; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.TypeExtensions.EnumExtensions; +using LCU.Library.Internal.TypeExtensions.ObjectExtensions; +using RateSheetCell = LCU.Library.Internal.LoanRateModels.Entities.RateSheetCell; + +namespace LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +public class RateSheetCellUploaderService : IRateSheetCellUploaderService +{ + private readonly IRateSheetService _rateSheetService; + private readonly IFileOperationsApiService _fileOperationsApiService; + + public RateSheetCellUploaderService(IRateSheetService rateSheetService, IFileOperationsApiService fileOperationsApiService) + { + _rateSheetService = rateSheetService; + _fileOperationsApiService = fileOperationsApiService; + } + + public async Task> UploadRateSheetCellsAsync(RateSheetCellUploadRequest uploadRequest) + { + try + { + var excelDataRequest = new ExcelDataRequest + { + FileContent = uploadRequest.FileContent, + SheetRequests = BuildSheetRequests(uploadRequest) + }; + + var excelDataResponse = await _fileOperationsApiService.DirectExtractExcelWorkBookData(excelDataRequest); + var rateSheetCells = await GetRateSheetCells(excelDataResponse, uploadRequest); + + return rateSheetCells; + } + catch (Exception ex) + { + var message = $"Failure during UploadRateSheetCellsAsync. Exception Message: {ex.Message}"; + Log.Error(message); + throw new ServiceException(message); + } + } + + private List BuildSheetRequests(RateSheetCellUploadRequest uploadRequest) + { + var sheetRequests = new List(); + foreach (var request in uploadRequest.Requests) + { + var rows = request.HeaderTopRows.DeepCopy(); + rows.AddRange(request.HeaderLeftRows); + rows = rows.Distinct().ToList(); + + var columns = request.HeaderTopColumns.DeepCopy(); + columns.AddRange(request.HeaderLeftColumns); + columns = columns.Distinct().ToList(); + + + sheetRequests.Add( + new() + { + SheetName = request.RateType.GetNameFromValue(), + Columns = columns, + Rows = rows + } + ); + } + return sheetRequests; + } + + private async Task> GetRateSheetCells(ExcelDataResponse excelDataResponse, RateSheetCellUploadRequest uploadRequest) + { + var rateSheets = (await _rateSheetService.GetIncludeRateSheetCellsAsync())?.ToList(); + foreach (var sheetResponse in excelDataResponse.WorkBook.SheetResponses) + { + var request = uploadRequest.Requests.FirstOrDefault(x => x.RateType.GetNameFromValue() == sheetResponse.SheetName); + + if (request == null) + { + continue; + } + + var rateSheet = rateSheets?.FirstOrDefault(rateSheet => rateSheet.RateType == request.RateType); + + if (rateSheet == null) + { + continue; + } + + foreach (var cell in sheetResponse.CellResponses) + { + if (!(request.HeaderTopRows.Contains(cell.Row) && + request.HeaderTopColumns.Contains(cell.ColumnName)) + && !(request.HeaderLeftRows.Contains(cell.Row) && + request.HeaderLeftColumns.Contains(cell.ColumnName))) + { + continue; + } + + var existingCell = rateSheet.RateSheetCells.FirstOrDefault(rateSheetCell => int.Parse(rateSheetCell.Row) == cell.Row + && rateSheetCell.Column == cell.ColumnName); + RateSheetCell rateSheetCell; + if (existingCell != null) + { + rateSheetCell = existingCell; + rateSheetCell.Value = FormatValue(cell.Value); + } + else + { + rateSheetCell = new RateSheetCell + { + RateSheetId = rateSheet.RateSheetId, + RateSheetCellType = RateSheetCellTypes.TopHeader, + Column = cell.ColumnName, + ColumnSpan = GetColumnSpan(sheetResponse.CellResponses, cell), + Row = cell.Row.ToString(), + RowSpan = GetRowSpan(sheetResponse.CellResponses, cell), + Value = FormatValue(cell.Value) + }; + rateSheet.RateSheetCells.Add(rateSheetCell); + } + } + } + + return (await _rateSheetService.UpdateRangeAsync(rateSheets)).ToList(); + } + + private int GetColumnSpan(List cellResponses, CellResponse cell) + { + var rowCells = cellResponses.Where(cellResponse => cellResponse.Row == cell.Row).ToList(); + var one = rowCells.Where(rowCell => string.Compare(rowCell.ColumnName, cell.ColumnName, StringComparison.InvariantCulture) > 0) + .ToList() + .Min(rc => rc.ColumnName); + + if (one is null || !one.Any()) + { + return 1; + } + + var two = cell.ColumnName.ToCharArray().First(); + return one.ToCharArray().FirstOrDefault() - two; + } + + private int GetRowSpan(List cellResponses, CellResponse cell) + { + var columnCells = cellResponses.Where(cellResponse => cellResponse.ColumnName == cell.ColumnName).ToList(); + var one = columnCells.OrderBy(x => x.Row).FirstOrDefault(rc => rc.Row > cell.Row)?.Row ?? cell.Row + 1; + var two = cell.Row; + return one - two; + } + + private string FormatValue(string value) + { + value = value.Trim(); + value = value.Replace("\n", "
"); + + return value; + } + +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateUploaderService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateUploaderService.cs new file mode 100644 index 0000000..21344e3 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateUploaderService.cs @@ -0,0 +1,305 @@ +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LcuEvaluation.Services; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Requests; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Responses; +using LCU.API.Internal.LoanRate.Services.MicroApiServices; +using LCU.Library.Internal.LoanRateModels.Enums; +using LCU.Library.Internal.LoanRateModels.Responses; +using LCU.Library.Internal.TypeExtensions.EnumExtensions; + +namespace LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; + +public class RateUploaderService : IRateUploaderService +{ + private readonly IRateService _rateService; + private readonly IRateSheetService _rateSheetService; + private readonly IEvaluationDetailService _evaluationDetailService; + private readonly IRateEvaluationService _rateEvaluationService; + private readonly IRateUploadInfoService _rateUploadInfoService; + private readonly IFileOperationsApiService _fileOperationsApiService; + + public RateUploaderService(IRateService rateService, + IRateSheetService rateSheetService, + IEvaluationDetailService evaluationDetailService, + IRateEvaluationService rateEvaluationService, + IRateUploadInfoService rateUploadInfoService, + IFileOperationsApiService fileOperationsApiService) + { + _rateService = rateService; + _rateSheetService = rateSheetService; + _evaluationDetailService = evaluationDetailService; + _rateEvaluationService = rateEvaluationService; + _rateUploadInfoService = rateUploadInfoService; + _fileOperationsApiService = fileOperationsApiService; + } + + public async Task UploadRates(RateUploadRequest request) + { + try + { + var rates = (await _rateService.GetByDateAsync(request.EffectiveDate))?.ToList(); + + if (rates is null || !rates.Any()) + { + throw new ServiceException($"No rates were found for the EffectiveDate: ({request.EffectiveDate:MM/dd/yyyy})"); + } + + var previousVersion = await _rateUploadInfoService.GetLatestVersionOnDate(request.EffectiveDate); + var rateUploadInfo = BuildNewRateUploadInfo(previousVersion?.Version, request.EffectiveDate, request.FileName, request.CreatedBy); + var rateSheets = (await _rateSheetService.GetAsync()).ToList(); + + var excelDataRequest = BuildExcelDataRequest(request.FileContent, rates, rateSheets); + var excelDataResponse = await _fileOperationsApiService.DirectExtractExcelWorkBookData(excelDataRequest); + + var countOfCellValues = 0; + foreach ( var sheet in excelDataResponse.WorkBook.SheetResponses ) + { + countOfCellValues += sheet.CellResponses.Count; + } + + if (rates.Count != countOfCellValues) + { + var message = $"Rate Count of {rates.Count} does not match the count of cell values returned {countOfCellValues}"; + Log.Error(message); + throw new ServiceException(message); + } + + await WorkThroughExcelDataResponseAndUpdateRates(excelDataResponse, rateUploadInfo, rates, rateSheets, request.EffectiveDate); + } + catch (Exception exception) + { + var message = $"Failure during UploadRates. RateExcel FileName: '{request.FileName}' Exception Message: {exception.Message}"; + Log.Error(message); + throw new ServiceException(message); + } + } + + public async Task UploadRates(RateUploadExcelDataRequest request) + { + try + { + var excelDataResponse = await _fileOperationsApiService.DirectExtractExcelWorkBookData(request.ExcelDataRequest); + var previousVersion = await _rateUploadInfoService.GetLatestVersionOnDate(request.EffectiveDate); + var rateUploadInfo = BuildNewRateUploadInfo(previousVersion?.Version, request.EffectiveDate, request.FileName, request.CreatedBy); + var rates = (await _rateService.GetByDateAsync(request.EffectiveDate))?.ToList(); + var rateSheets = (await _rateSheetService.GetAsync()).ToList(); + await WorkThroughExcelDataResponseAndUpdateRates(excelDataResponse, rateUploadInfo, rates, rateSheets, request.EffectiveDate); + } + catch (Exception exception) + { + var message = $"Failure during UploadRates. Exception Message: {exception.Message}"; + Log.Error(message); + throw new ServiceException(message); + } + } + + public ExcelDataRequest BuildExcelDataRequest(string rateExcelContent, List rates, List rateSheets) + { + var sheetRequests = new List(); + foreach (var rate in rates) + { + var rateSheet = rateSheets.FirstOrDefault(x => x.RateSheetId == rate.RateSheetId); + + if (rateSheet is null) + { + throw new ServiceException($"Rate Sheet Id {rate.RateSheetId} does not exist"); + } + + var sheetRequest = sheetRequests.FirstOrDefault(sheet => sheet.SheetName == rateSheet.SheetName); + if (sheetRequest == null) + { + sheetRequest = new SheetRequest(); + sheetRequest.Rows = new List(); + sheetRequest.Columns = new List(); + sheetRequest.SheetName = rateSheet.SheetName; + sheetRequests.Add(sheetRequest); + } + + var intRow = Convert.ToInt32(rate.Row); + if (!sheetRequest.Rows.Contains(intRow)) + { + sheetRequest.Rows.Add(intRow); + } + + if (!sheetRequest.Columns.Contains(rate.Column)) + { + sheetRequest.Columns.Add(rate.Column); + } + } + + var excelDataRequest = new ExcelDataRequest + { + FileContent = rateExcelContent, + SheetRequests = sheetRequests, + }; + + return excelDataRequest; + } + + public async Task WorkThroughExcelDataResponseAndUpdateRates(ExcelDataResponse excelDataResponse, + RateUploadInfo rateUploadInfo, + List rates, + List rateSheets, + DateTime effectiveDate) + { + if (excelDataResponse.WorkBook?.SheetResponses is null) + { + return; + } + + var ratesToCreate = new List(); + var ratesToClose = new List(); + foreach (var sheet in excelDataResponse.WorkBook.SheetResponses) + { + var rateSheet = rateSheets.FirstOrDefault(x => x.SheetName == sheet.SheetName); + if (rateSheet is null) + { + var message = $"RateSheet with SheetName: {sheet.SheetName} does not exist."; + Log.Error(message); + throw new ServiceException(message); + } + + foreach (var cell in sheet.CellResponses) + { + if (!decimal.TryParse(cell.Value, out decimal newDecimalValue)) + { + var message = $"Cell value was not able to be parsed into a decimal. " + + $"Column:{cell.ColumnName}, Row:{cell.Row}, Value:{cell.Value}"; + Log.Error(message); + throw new ServiceException(message); + } + + var rate = rates.FirstOrDefault(requestedRate => requestedRate.RateSheetId == rateSheet.RateSheetId && + requestedRate.Row == cell.Row.ToString() && + string.Equals(requestedRate.Column, cell.ColumnName, StringComparison.OrdinalIgnoreCase)); + + if (rate is null) + { + var newRate = new Rate + { + Column = cell.ColumnName, + Row = cell.Row.ToString(), + Value = newDecimalValue, + RateStartDate = effectiveDate, + RateSheetId = rateSheet.RateSheetId + }; + + ratesToCreate.Add(newRate); + + continue; + } + + var isNewValue = rate.Value != Math.Round(newDecimalValue, 2); + if (!isNewValue) + { + continue; + } + + var rateToCreate = rate.DeepCopyNewRate(); + rateToCreate.Value = newDecimalValue; + rateToCreate.RateStartDate = effectiveDate; + ratesToCreate.Add(rateToCreate); + + if (rate.RateEndDate != null) + { + throw new ServiceException($"Previous rate, with RateId {rate.RateId}, already has an end date " + + $"of {rate.RateEndDate:MM/dd/yyyy}. This should not occur."); + } + + rate.RateEndDate = effectiveDate; + ratesToClose.Add(rate); + } + } + + if (ratesToCreate.Count > 0) + { + rateUploadInfo.Rates = ratesToCreate; + rateUploadInfo.RateCount = rateUploadInfo.Rates.Count; + } + + if (ratesToClose.Count > 0) + { + rateUploadInfo.ClosedRates = ratesToClose; + } + + if (rateUploadInfo.ClosedRates?.Count > 0 || rateUploadInfo.Rates?.Count > 0) + { + await _rateUploadInfoService.SaveAsync(rateUploadInfo); + } + } + + public async Task> InitiateRatesEvaluations(IEnumerable requests) + { + try + { + var newRateEvaluations = new List(); + var responses = new List(); + + foreach (var request in requests) + { + var response = new InitiateRatesEvaluationsResponse { Request = request }; + var rateType = request.SheetName.GetValueFromName(); + var existingRates = (await _rateSheetService.GetRatesByRateTypeAsync(rateType))?.ToList(); + + var rule = await _evaluationDetailService.GetByIdAsync(request.EvaluationDetailId); + if (rule is null) + { + response.AddSuccessMessage($"Rule was not found with EvaluationDetailId: {request.EvaluationDetailId}"); + responses.Add(response); + continue; + } + + foreach (var rowsOrColumns in request.RowsOrColumns) + { + var foundRates = request.IsRowsRule ? existingRates?.Where(rate => rate.Row == rowsOrColumns).ToList() + : existingRates?.Where(rate => rate.Column == rowsOrColumns).ToList(); + + if (foundRates is null || !foundRates.Any()) + { + response.AddFailureMessage($"Rule does not exist with EvaluationDetailId: {request.EvaluationDetailId}"); + continue; + } + + foreach (var foundRate in foundRates) + { + var rateRuleAlreadyExist = foundRate.RateEvaluations.FirstOrDefault(rate => rate.EvaluationDetailId == request.EvaluationDetailId) is not null; + if (rateRuleAlreadyExist) + { + response.AddSuccessMessage($"RateEvaluation already exists with EvaluationDetailId: {request.EvaluationDetailId}"); + continue; + } + + var newRateEvaluation = new RateEvaluation + { + EvaluationDetailId = request.EvaluationDetailId, + RateId = foundRate.RateId + }; + + response.RateEvaluationResults.Add(newRateEvaluation); + newRateEvaluations.Add(newRateEvaluation); + } + } + responses.Add(response); + } + + await _rateEvaluationService.SaveRangeAsync(newRateEvaluations); + return responses; + } + catch (Exception exception) + { + var message = "Failure Initiating RatesEvaluations."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + private RateUploadInfo BuildNewRateUploadInfo(int? previousVersion, DateTime effectiveDate, string fileName, string createdBy) + => new () + { + Version = (previousVersion ?? 0) + 1, + EffectiveDate = effectiveDate, + Filename = fileName, + TimeCreated = DateTime.Now, + CreatedBy = createdBy + }; +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateValidationService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateValidationService.cs new file mode 100644 index 0000000..729dfe4 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/LoanRateOperationServices/RateValidationService.cs @@ -0,0 +1,72 @@ +using LCU.Library.Internal.LoanRateModels.Responses.Validation; +using LCU.Library.Internal.LoanRateModels.Responses.Validation.RateSheetValidations; +using Rate = LCU.Library.Internal.LoanRateModels.Entities.Rate; + +namespace LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +public class RateValidationService : IRateValidationService +{ + private readonly IRateService _rateService; + + public RateValidationService(IRateService rateService) + { + _rateService = rateService; + } + + public async Task> ValidateAsync(List rates) + { + + try + { + var result = new List(); + foreach (var rate in rates) + { + var validation = new RateSheetValidationResult + { + NewItem = rate + }; + + var rateHistory = (await _rateService.GetRateHistory(rate))?.ToList(); + if (rateHistory is null || rateHistory.Count == 0) + { + validation.Message = "Rate history could not be found."; + result.Add(validation); + validation.Pass = false; + continue; + } + + rateHistory = rateHistory.OrderBy(x => x.RateStartDate) + .ThenBy(x => x.RateEndDate) + .ToList(); + + var originalRate = await _rateService.GetByIdAsync(rate.RateId); + if (originalRate is null) + { + validation.Message = "Original rate could not be found."; + result.Add(validation); + validation.Pass = false; + continue; + } + + validation.OriginalItem = originalRate; + + validation.Validations = new List + { + RateSheetValidation.IsWithinTrendRange(rate.Value, originalRate.Value, rateHistory.Select(x => x.Value).ToList()), + RateSheetValidation.IsWithinRange(rate.Value, rateHistory.Min(x => x.Value), rateHistory.Max(x => x.Value)) + }; + + validation.CheckValidation(); + result.Add(validation); + } + + return result; + + } + catch (Exception exception) + { + var message = "Failure to validate rates."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/MicroApiServices/FileOperationsApiService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/MicroApiServices/FileOperationsApiService.cs new file mode 100644 index 0000000..8d02c42 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/MicroApiServices/FileOperationsApiService.cs @@ -0,0 +1,28 @@ +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Requests; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Responses; +using LCU.API.Internal.LoanRate.DataAccess; +namespace LCU.API.Internal.LoanRate.Services.MicroApiServices; + +public class FileOperationsApiService : IFileOperationsApiService +{ + private readonly IFileOperationsApiDao _fileOperationsApiDao; + + public FileOperationsApiService(IFileOperationsApiDao fileOperationsApiDao) + { + _fileOperationsApiDao = fileOperationsApiDao; + } + + public async Task DirectExtractExcelWorkBookData(ExcelDataRequest request) + { + try + { + return await _fileOperationsApiDao.DirectExtractExcelWorkBookData(request); + } + catch (Exception exception) + { + var error = $"Failure during DirectExtractExcelWorkBookData. Exception Message: {exception.Message}"; + Log.Error(error); + throw new ServiceException(error); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/MicroApiServices/IFileOperationsApiService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/MicroApiServices/IFileOperationsApiService.cs new file mode 100644 index 0000000..c65dfce --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/MicroApiServices/IFileOperationsApiService.cs @@ -0,0 +1,8 @@ +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Requests; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Responses; +namespace LCU.API.Internal.LoanRate.Services.MicroApiServices; + +public interface IFileOperationsApiService +{ + Task DirectExtractExcelWorkBookData(ExcelDataRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/IQueueLoadTestInitiateService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/IQueueLoadTestInitiateService.cs new file mode 100644 index 0000000..26fdac5 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/IQueueLoadTestInitiateService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +namespace LCU.API.Internal.LoanRate.Services.QueueServices; + +public interface IQueueLoadTestInitiateService +{ + Task QueueLoadTestInitiateRequest(LoadTestInitiateRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/IQueueLoadTestProcessRateService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/IQueueLoadTestProcessRateService.cs new file mode 100644 index 0000000..f5a21ad --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/IQueueLoadTestProcessRateService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +namespace LCU.API.Internal.LoanRate.Services.QueueServices; + +public interface IQueueLoadTestProcessRateService +{ + Task QueueLoadTestProcessRateRequest(LoadTestProcessRateRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/IQueueLoadTestRateCompletionService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/IQueueLoadTestRateCompletionService.cs new file mode 100644 index 0000000..428601c --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/IQueueLoadTestRateCompletionService.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +namespace LCU.API.Internal.LoanRate.Services.QueueServices; + +public interface IQueueLoadTestRateCompletionService +{ + Task QueueLoadTestRateCompletionRequest(LoadTestRateCompletionRequest request); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/QueueLoadTestInitiateService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/QueueLoadTestInitiateService.cs new file mode 100644 index 0000000..3b3d06d --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/QueueLoadTestInitiateService.cs @@ -0,0 +1,32 @@ +using MassTransit; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +using LCU.Api.Internal.MaidBotLib.Models; +using LCU.API.Internal.LoanRate.Configurations; +namespace LCU.API.Internal.LoanRate.Services.QueueServices; + +public class QueueLoadTestInitiateService : IQueueLoadTestInitiateService +{ + private readonly ISendEndpoint _sendEndpoint; + + public QueueLoadTestInitiateService(IBusControl busControl, + ReceiveEndpointConfigurations receiveEndpointConfigurations) + { + _sendEndpoint = busControl.GetSendEndpoint(receiveEndpointConfigurations.LoadTestInitiateReceiveEndpoint.ReceiveEndpointUri).Result; + } + + public async Task QueueLoadTestInitiateRequest(LoadTestInitiateRequest request) + { + try + { + var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + await _sendEndpoint.Send(request, cancellationTokenSource.Token); + } + catch (Exception exception) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), + Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Fatal(exception, "Failed to Queue Load Test Initiate Request."); + throw new ServiceException("Failed to Queue Load Test Initiate Request."); + } + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/QueueLoadTestProcessRateService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/QueueLoadTestProcessRateService.cs new file mode 100644 index 0000000..022e5e5 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/QueueLoadTestProcessRateService.cs @@ -0,0 +1,32 @@ +using MassTransit; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +using LCU.Api.Internal.MaidBotLib.Models; +using LCU.API.Internal.LoanRate.Configurations; +namespace LCU.API.Internal.LoanRate.Services.QueueServices; + +public class QueueLoadTestProcessRateService : IQueueLoadTestProcessRateService +{ + private readonly ISendEndpoint _sendEndpoint; + + public QueueLoadTestProcessRateService(IBusControl busControl, + ReceiveEndpointConfigurations receiveEndpointConfigurations) + { + _sendEndpoint = busControl.GetSendEndpoint(receiveEndpointConfigurations.LoadTestProcessRateReceiveEndpoint.ReceiveEndpointUri).Result; + } + + public async Task QueueLoadTestProcessRateRequest(LoadTestProcessRateRequest request) + { + try + { + var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + await _sendEndpoint.Send(request, cancellationTokenSource.Token); + } + catch (Exception exception) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), + Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Fatal(exception, "Failed to Queue Load Test Process Rate Request."); + throw new ServiceException("Failed to Queue Load Test Process Rate Request."); + } + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/QueueLoadTestRateCompletionService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/QueueLoadTestRateCompletionService.cs new file mode 100644 index 0000000..9a6a7d2 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/QueueServices/QueueLoadTestRateCompletionService.cs @@ -0,0 +1,32 @@ +using MassTransit; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +using LCU.Api.Internal.MaidBotLib.Models; +using LCU.API.Internal.LoanRate.Configurations; +namespace LCU.API.Internal.LoanRate.Services.QueueServices; + +public class QueueLoadTestRateCompletionService : IQueueLoadTestRateCompletionService +{ + private readonly ISendEndpoint _sendEndpoint; + + public QueueLoadTestRateCompletionService(IBusControl busControl, + ReceiveEndpointConfigurations receiveEndpointConfigurations) + { + _sendEndpoint = busControl.GetSendEndpoint(receiveEndpointConfigurations.LoadTestRateCompletionReceiveEndpoint.ReceiveEndpointUri).Result; + } + + public async Task QueueLoadTestRateCompletionRequest(LoadTestRateCompletionRequest request) + { + try + { + var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + await _sendEndpoint.Send(request, cancellationTokenSource.Token); + } + catch (Exception exception) + { + Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group), + Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment)) + .Fatal(exception, "Failed to Queue Load Test Rate Completion Request."); + throw new ServiceException("Failed to Queue Load Test Rate Completion Request."); + } + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateEvaluationService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateEvaluationService.cs new file mode 100644 index 0000000..da193a6 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateEvaluationService.cs @@ -0,0 +1,311 @@ +using Microsoft.Data.SqlClient; +using LCU.API.Internal.LoanRateLib.Repositories; +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.API.Internal.LoanRate.Services; + +public class RateEvaluationService : IRateEvaluationService +{ + private readonly IUnitOfWork _unitOfWork; + private readonly IRateEvaluationRepository _rateEvaluationRepository; + private readonly IRateService _rateService; + + public RateEvaluationService(IUnitOfWork unitOfWork, + IRateEvaluationRepository rateEvaluationRepository, + IRateService rateService) + { + _unitOfWork = unitOfWork; + _rateEvaluationRepository = rateEvaluationRepository; + _rateService = rateService; + } + + public async Task> GetAsync() + { + try + { + return await _rateEvaluationRepository.FindAsync() ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving RateEvaluations."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task GetByIdAsync(int id) + { + try + { + return await _rateEvaluationRepository.FindByIdAsync(id); + } + catch (Exception exception) + { + var message = $"Failure retrieving RateEvaluation with id: {id}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task> GetRangeByIdAsync(List ids) + { + try + { + return await _rateEvaluationRepository.FindRangeByIdAsync(ids) ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving RateEvaluations. " + + $"Ids: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task> GetRangeByEvaluationDetailIdAsync(int evaluationDetailId) + { + try + { + return await _rateEvaluationRepository.FindRangeByEvaluationDetailIdAsync(evaluationDetailId) ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving RateEvaluations by EvaluationDetailId. " + + $"EvaluationDetailId: {evaluationDetailId}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task> GetByRateSheetIdsAsync(List rateSheetIds) + { + try + { + return await _rateEvaluationRepository.FindByRateSheetIdsAsync(rateSheetIds) ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving RateEvaluations in GetByRateSheetIdsAsync. " + + $"RateSheetIds: {string.Join(", ",rateSheetIds)}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task SaveAsync(RateEvaluation rateEvaluation) + { + try + { + var result = await _rateEvaluationRepository.AddAsync(rateEvaluation); + + if (result is null) + { + throw new Exception("Saving a RateEvaluation returned null."); + } + + await _unitOfWork.CompleteAsync(); + _rateService.ClearCache(); + + return result; + } + catch (Exception exception) + { + var message = "Failure saving RateEvaluation."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task SaveRangeAsync(IEnumerable rateEvaluations) + { + try + { + await _rateEvaluationRepository.AddRangeAsync(rateEvaluations); + + _rateService.ClearCache(); + await _unitOfWork.CompleteAsync(); + } + catch (Exception exception) + { + var message = "Failure saving list of RateEvaluations."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task UpdateAsync(RateEvaluation rateEvaluation) + { + try + { + var existing = await GetByIdAsync(rateEvaluation.RateEvaluationId); + if (existing is null) + { + Log.Error($"RateEvaluation with id:{rateEvaluation.RateEvaluationId} was not found."); + return null; + } + + existing.RateId = rateEvaluation.RateId; + existing.EvaluationDetailId = rateEvaluation.EvaluationDetailId; + + await _unitOfWork.CompleteAsync(); + _rateService.ClearCache(); + + return existing; + } + catch (SqlException sqlException) + { + var message = $"SQL Failure updating RateEvaluation. RateEvaluationId: {rateEvaluation.RateEvaluationId}"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = $"Failure updating RateEvaluation. RateEvaluationId: {rateEvaluation.RateEvaluationId}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task> UpdateRangeAsync(IEnumerable rateEvaluations) + { + try + { + var records = rateEvaluations.ToList(); + + _rateEvaluationRepository.UpdateRange(records); + + await _unitOfWork.CompleteAsync(); + _rateService.ClearCache(); + + return records; + } + catch (SqlException sqlException) + { + var message = "SQL Failure updating list of RateEvaluations."; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = "Failure updating list of RateEvaluations."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task DeleteAsync(int id) + { + try + { + var rateEvaluation = await GetByIdAsync(id); + ArgumentNullException.ThrowIfNull(rateEvaluation); + + _rateEvaluationRepository.Remove(rateEvaluation); + + await _unitOfWork.CompleteAsync(); + _rateService.ClearCache(); + } + catch (SqlException sqlException) + { + var message = $"SQL failure removing RateEvaluation. RateEvaluationId: {id}"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = $"Failure removing RateEvaluation. RateEvaluationId: {id}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task DeleteRangeAsync(List ids) + { + try + { + var rateEvaluations = await GetRangeByIdAsync(ids); + + if (rateEvaluations is null || !rateEvaluations.Any()) + { + throw new Exception("GetRangeByIdAsync returned no RateEvaluations to remove."); + } + + _rateEvaluationRepository.RemoveRange(rateEvaluations); + + await _unitOfWork.CompleteAsync(); + _rateService.ClearCache(); + } + catch (SqlException sqlException) + { + var message = "SQL failure removing list of RateEvaluations. " + + $"RateEvaluationIds: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = "Failure removing list of RateEvaluations. " + + $"RateEvaluationIds: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task DeleteRangeByEvaluationDetailIdAsync(int evaluationDetailId) + { + try + { + if (evaluationDetailId <= 0) + { + throw new Exception("EvaluationDetailId must be greater than 0."); + } + + var rateEvaluations = (await _rateEvaluationRepository.FindRangeByEvaluationDetailIdAsync(evaluationDetailId))?.ToList(); + + if (rateEvaluations is null || !rateEvaluations.Any()) + { + Log.Warning("No RateEvaluations were found for DeleteRangeByEvaluationDetailIdAsync"); + return; + } + + await DeleteRangeAsync(rateEvaluations.Select( x => x.RateEvaluationId ).ToList()); + _rateService.ClearCache(); + } + catch (SqlException sqlException) + { + var message = "SQL failure removing list of RateEvaluations. " + + $"EvaluationDetailId: {evaluationDetailId}"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = "Failure removing list of RateEvaluations. " + + $"EvaluationDetailId: {evaluationDetailId}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task CountByEvaluationDetailIdAsync(int evaluationDetailId) + { + try + { + return await _rateEvaluationRepository.CountByEvaluationDetailIdAsync(evaluationDetailId); + } + catch (SqlException sqlException) + { + var message = "SQL failure on CountByEvaluationDetailIdAsync. " + + $"EvaluationDetailId: ({evaluationDetailId})"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = "Failure on CountByEvaluationDetailIdAsync. " + + $"EvaluationDetailId: ({evaluationDetailId})"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateProcessingInfoService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateProcessingInfoService.cs new file mode 100644 index 0000000..8bd4ab5 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateProcessingInfoService.cs @@ -0,0 +1,226 @@ +using Microsoft.Data.SqlClient; +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +using LCU.API.Internal.LoanRateLoadTestingLib.Repositories; +namespace LCU.API.Internal.LoanRate.Services; + +public class RateProcessingInfoService : IRateProcessingInfoService +{ + private readonly IUnitOfWork _unitOfWork; + private readonly IRateProcessingInfoRepository _rateProcessingInfoRepository; + + public RateProcessingInfoService(IUnitOfWork unitOfWork, + IRateProcessingInfoRepository rateProcessingInfoRepository) + { + _unitOfWork = unitOfWork; + _rateProcessingInfoRepository = rateProcessingInfoRepository; + } + + public async Task> GetAsync() + { + try + { + return await _rateProcessingInfoRepository.FindAsync() ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving RateProcessingInfos."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task GetByIdAsync(int id) + { + try + { + return await _rateProcessingInfoRepository.FindByIdAsync(id); + } + catch (Exception exception) + { + var message = $"Failure retrieving RateProcessingInfo with id: {id}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task> GetRangeByIdAsync(List ids) + { + try + { + return await _rateProcessingInfoRepository.FindRangeByIdAsync(ids) ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving RateProcessingInfos. " + + $"Ids: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task?> GetByLoadTestRunInfoId(int loadTestRunInfoId) + { + try + { + return await _rateProcessingInfoRepository.FindByLoadTestRunInfoIdAsync(loadTestRunInfoId) ?? new List(); + } + catch (Exception exception) + { + var message = $"Failure retrieving RateProcessingInfo with loadTestRunInfoId: {loadTestRunInfoId}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task SaveAsync(RateProcessingInfo rateProcessingInfo) + { + try + { + var result = await _rateProcessingInfoRepository.AddAsync(rateProcessingInfo); + + if (result is null) + { + throw new Exception("Saving a RateProcessingInfo returned null."); + } + + await _unitOfWork.CompleteAsync(); + return result; + } + catch (Exception exception) + { + var message = "Failure saving RateProcessingInfo."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task SaveRangeAsync(IEnumerable rateProcessingInfos) + { + try + { + await _rateProcessingInfoRepository.AddRangeAsync(rateProcessingInfos); + await _unitOfWork.CompleteAsync(); + } + catch (Exception exception) + { + var message = "Failure saving list of RateProcessingInfos."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task UpdateAsync(RateProcessingInfo rateProcessingInfo) + { + try + { + var existing = await GetByIdAsync(rateProcessingInfo.RateProcessingInfoId); + if (existing is null) + { + Log.Error($"RateProcessingInfo with id:{rateProcessingInfo.RateProcessingInfoId} was not found."); + return null; + } + + existing.LoadTestRunInfoId = rateProcessingInfo.LoadTestRunInfoId; + existing.Payload = rateProcessingInfo.Payload; + existing.Rate = rateProcessingInfo.Rate; + existing.Column = rateProcessingInfo.Column; + existing.Row = rateProcessingInfo.Row; + existing.RateProcessingStatusType = rateProcessingInfo.RateProcessingStatusType; + existing.RateProcessingResultType = rateProcessingInfo.RateProcessingResultType; + existing.ProcessingMessage = rateProcessingInfo.ProcessingMessage; + + await _unitOfWork.CompleteAsync(); + return existing; + } + catch (SqlException sqlException) + { + var message = $"SQL Failure updating RateProcessingInfo. RateProcessingInfoId: {rateProcessingInfo.RateProcessingInfoId}"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = $"Failure updating RateProcessingInfo. RateProcessingInfoId: {rateProcessingInfo.RateProcessingInfoId}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task> UpdateRangeAsync(IEnumerable rateProcessingInfos) + { + try + { + var records = rateProcessingInfos.ToList(); + + _rateProcessingInfoRepository.UpdateRange(records); + await _unitOfWork.CompleteAsync(); + + return records; + } + catch (SqlException sqlException) + { + var message = "SQL Failure updating list of RateProcessingInfos."; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = "Failure updating list of RateProcessingInfos."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task DeleteAsync(int id) + { + try + { + var rateProcessingInfo = await GetByIdAsync(id); + ArgumentNullException.ThrowIfNull(rateProcessingInfo); + _rateProcessingInfoRepository.Remove(rateProcessingInfo); + await _unitOfWork.CompleteAsync(); + } + catch (SqlException sqlException) + { + var message = $"SQL failure removing RateProcessingInfo. RateProcessingInfoId: {id}"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = $"Failure removing RateProcessingInfo. RateProcessingInfoId: {id}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task DeleteRangeAsync(List ids) + { + try + { + var rateProcessingInfos = await GetRangeByIdAsync(ids); + + if (rateProcessingInfos is null || !rateProcessingInfos.Any()) + { + throw new Exception("GetRangeByIdAsync returned no RateProcessingInfos to remove."); + } + + _rateProcessingInfoRepository.RemoveRange(rateProcessingInfos); + await _unitOfWork.CompleteAsync(); + } + catch (SqlException sqlException) + { + var message = "SQL failure removing list of RateProcessingInfos. " + + $"RateProcessingInfoIds: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = "Failure removing list of RateProcessingInfos. " + + $"RateProcessingInfoIds: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateService.cs new file mode 100644 index 0000000..4a1bfdd --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateService.cs @@ -0,0 +1,335 @@ +using Microsoft.Data.SqlClient; +using LCU.API.Internal.LoanRateLib.Repositories; +using LCU.Library.Internal.LcuEvaluationModels.Enums; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.TypeExtensions.EnumExtensions; +using Newtonsoft.Json; + +namespace LCU.API.Internal.LoanRate.Services; + +public class RateService : IRateService +{ + private readonly IUnitOfWork _unitOfWork; + private readonly IRateRepository _rateRepository; + private readonly IRateSheetService _rateSheetService; + private readonly ILoadTestRunInfoService _loadTestRunInfoService; + + public RateService(IUnitOfWork unitOfWork, + IRateRepository rateRepository, + IRateSheetService rateSheetService, + ILoadTestRunInfoService loadTestRunInfoService) + { + _unitOfWork = unitOfWork; + _rateRepository = rateRepository; + _rateSheetService = rateSheetService; + _loadTestRunInfoService = loadTestRunInfoService; + } + + public async Task> GetAsync() + { + try + { + return await _rateRepository.FindAsync() ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving Rates."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task GetByIdAsync(int id) + { + try + { + return await _rateRepository.FindByIdAsync(id); + } + catch (Exception exception) + { + var message = $"Failure retrieving Rate with id: {id}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task?> GetByDateAsync(DateTime date) + { + try + { + return await _rateRepository.FindByDateAsync(date) ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving Rates."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task?> GetRateHistory(Rate rate) + { + try + { + return await _rateRepository.FindRateHistoryAsync(rate) ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving rate history. " + + $"Rate: {JsonConvert.SerializeObject(rate)}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task> GetRangeByIdAsync(List ids) + { + try + { + return await _rateRepository.FindRangeByIdAsync(ids) ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving Rates. " + + $"Ids: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task?> GetByClosingRateUploadInfoIdAsync(int id) + { + try + { + return await _rateRepository.FindByClosingRateUploadInfoIdAsync(id) ?? new List(); + } + catch (Exception exception) + { + var message = $"Failure retrieving Rates by ClosingRateUploadInfoId with id {id}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task?> GetByDateEvaluationIdAndEvaluationTypeAsync(DateTime date, + List evaluationIds, + EvaluationTypes evaluationType) + { + try + { + return await _rateRepository.FindByDateEvaluationIdsAndEvaluationTypeAsync(date, evaluationIds, evaluationType) ?? new List(); + } + catch (Exception exception) + { + var message = "Failure retrieving Rates. " + + $"Ids: ({string.Join(",", evaluationIds.Select(n => n.ToString()).ToArray())})." + + $"EvaluationType: {evaluationType} "; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task>?> GetByLoadTestRunInfoId(int loadTestRunInfoId) + { + try + { + var loadTestRunInfo = await _loadTestRunInfoService.GetByIdAsync(loadTestRunInfoId); + + if (loadTestRunInfo is null) + { + return new Dictionary>(); + } + + var rateRequests = loadTestRunInfo.RateProcessingInfos.Select(x => JsonConvert.DeserializeObject(x.Payload)).ToList(); + var applicationDates = rateRequests.Select(x => x.ApplicationDate).Distinct(); + + var response = new Dictionary>(); + foreach (var date in applicationDates) + { + var rates = (await _rateSheetService.GetRatesByDateAndRateTypeAsync(date, loadTestRunInfo.RateType) ?? new List()).ToList(); + response.Add(date, rates); + } + + return response; + } + catch (Exception exception) + { + var message = "Failure retrieving Rates by LoadTestRunInfoId. " + + $"LoadTestRunInfoId: ({loadTestRunInfoId})."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task SaveAsync(Rate rate) + { + try + { + var result = await _rateRepository.AddAsync(rate); + + if (result is null) + { + throw new Exception("Saving a Rate returned null."); + } + + await _unitOfWork.CompleteAsync(); + ClearCache(); + return result; + } + catch (Exception exception) + { + var message = "Failure saving Rate."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task SaveRangeAsync(IEnumerable rates) + { + try + { + await _rateRepository.AddRangeAsync(rates); + await _unitOfWork.CompleteAsync(); + ClearCache(); + } + catch (Exception exception) + { + var message = "Failure saving list of Rates."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task UpdateAsync(Rate rate) + { + try + { + var existing = await GetByIdAsync(rate.RateId); + if (existing is null) + { + Log.Error($"Rate with id:{rate.RateId} was not found."); + return null; + } + + existing.RateUploadInfoId = rate.RateUploadInfoId; + existing.ClosingRateUploadInfoId = rate.ClosingRateUploadInfoId; + existing.Column = rate.Column; + existing.Row = rate.Row; + existing.RateSheetId = rate.RateSheetId; + existing.Value = rate.Value; + existing.RateStartDate = rate.RateStartDate; + existing.RateEndDate = rate.RateEndDate; + + await _unitOfWork.CompleteAsync(); + ClearCache(); + return existing; + } + catch (SqlException sqlException) + { + var message = $"SQL Failure updating Rate. RateId: {rate.RateId}"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = $"Failure updating Rate. RateId: {rate.RateId}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task> UpdateRangeAsync(IEnumerable rates) + { + try + { + var records = rates.ToList(); + + _rateRepository.UpdateRange(records); + await _unitOfWork.CompleteAsync(); + ClearCache(); + return records; + } + catch (SqlException sqlException) + { + var message = "SQL Failure updating list of Rates."; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = "Failure updating list of Rates."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task DeleteAsync(int id) + { + try + { + var rate = await GetByIdAsync(id); + ArgumentNullException.ThrowIfNull(rate); + _rateRepository.Remove(rate); + await _unitOfWork.CompleteAsync(); + ClearCache(); + } + catch (SqlException sqlException) + { + var message = $"SQL failure removing Rate. RateId: {id}"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = $"Failure removing Rate. RateId: {id}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task DeleteRangeAsync(List ids) + { + try + { + var rates = await GetRangeByIdAsync(ids); + + if (rates is null || !rates.Any()) + { + throw new Exception("GetRangeByIdAsync returned no Rates to remove."); + } + + _rateRepository.RemoveRange(rates); + await _unitOfWork.CompleteAsync(); + ClearCache(); + } + catch (SqlException sqlException) + { + var message = "SQL failure removing list of Rates. " + + $"RateIds: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})"; + Log.Error(sqlException, message); + throw new ServiceException(message); + } + catch (Exception exception) + { + var message = "Failure removing list of Rates. " + + $"RateIds: ({string.Join(",", ids.Select(n => n.ToString()).ToArray())})"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public void ClearCache() + { + try + { + _rateRepository.ClearCache(); + } + catch (Exception exception) + { + var message = "Failure to clear cache for Rates"; + + Log.Error(exception, message); + throw new ServiceException(message); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateSheetCellService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateSheetCellService.cs new file mode 100644 index 0000000..dc29832 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateSheetCellService.cs @@ -0,0 +1,14 @@ +using LCU.API.Internal.LoanRateLib.Repositories; +using LCU.Library.Internal.LcuClassPatterns.Crud.Services; + +namespace LCU.API.Internal.LoanRate.Services; + +public class RateSheetCellService : CrudService, IRateSheetCellService +{ + private readonly IRateSheetCellRepository _rateSheetCellRepository; + + public RateSheetCellService(IRateSheetCellRepository rateSheetCellRepository) : base(rateSheetCellRepository) + { + _rateSheetCellRepository = rateSheetCellRepository; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateSheetService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateSheetService.cs new file mode 100644 index 0000000..d20ff1c --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateSheetService.cs @@ -0,0 +1,114 @@ +using LCU.API.Internal.LoanRateLib.Repositories; +using LCU.Library.Internal.LcuClassPatterns.Crud.Services; +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.API.Internal.LoanRate.Services; + +public class RateSheetService : CrudService, IRateSheetService +{ + private readonly IRateSheetRepository _rateSheetRepository; + + public RateSheetService(IRateSheetRepository rateSheetRepository) : base(rateSheetRepository) + { + _rateSheetRepository = rateSheetRepository; + } + + public async Task?> GetIncludeAllAsync() + { + try + { + return await _rateSheetRepository.FindIncludeAllAsync() ?? new List(); + } + catch (Exception exception) + { + var message = "Failure during GetIncludeAllAsync."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task?> GetIncludeRateSheetCellsAsync() + { + try + { + return await _rateSheetRepository.FindIncludeRateSheetCellsAsync() ?? new List(); + } + catch (Exception exception) + { + var message = "Failure during GetIncludeRateSheetCellsAsync."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task?> GetRatesByRateTypeAsync(RateTypes rateType) + { + try + { + return await _rateSheetRepository.FindRatesByRateType(rateType) ?? new List(); + } + catch (Exception exception) + { + var message = $"Failure retrieving Rates with RateType: {rateType}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task?> GetRateSheetIdsByRateTypeAsync(RateTypes rateType) + { + try + { + return await _rateSheetRepository.FindRateSheetIdsByRateType(rateType) ?? new List(); + } + catch (Exception exception) + { + var message = $"Failure retrieving RateSheetIds with RateType: {rateType}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task?> GetRatesByDateAndRateTypeAsync(DateTime date, RateTypes rateType) + { + try + { + return await _rateSheetRepository.FindRatesByDateAndRateType(date, rateType) ?? new List(); + } + catch (Exception exception) + { + var message = $"Failure retrieving Rates with RateType ({rateType}) and Date: {date::MM-dd-yyyy HH:mm:ss}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task GetByDateAndRateTypeAsync(DateTime date, RateTypes rateType) + { + try + { + return await _rateSheetRepository.FindByDateAndRateType(date, rateType); + } + catch (Exception exception) + { + var message = $"Failure retrieving RateSheet with RateType ({rateType}) and Date: {date::MM-dd-yyyy HH:mm:ss}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public void ClearCache() + { + try + { + _rateSheetRepository.ClearCache(); + } + catch (Exception exception) + { + var message = "Failure to clear cache for RateSheets"; + + Log.Error(exception, message); + throw new ServiceException(message); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateUploadInfoService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateUploadInfoService.cs new file mode 100644 index 0000000..f3b280d --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/RateUploadInfoService.cs @@ -0,0 +1,166 @@ +using LCU.API.Internal.LoanRateLib.Repositories; +using LCU.Library.Internal.LcuClassPatterns.Crud.Services; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LoanRateModels.Responses; +using LCU.Library.Internal.TypeExtensions.ObjectExtensions; +using Newtonsoft.Json; + +namespace LCU.API.Internal.LoanRate.Services; + +public class RateUploadInfoService : CrudService, IRateUploadInfoService +{ + private readonly IRateUploadInfoRepository _rateUploadInfoRepository; + private readonly IRateService _rateService; + + public RateUploadInfoService(IRateUploadInfoRepository rateUploadInfoRepository, + IRateService rateService) : base(rateUploadInfoRepository) + { + _rateUploadInfoRepository = rateUploadInfoRepository; + _rateService = rateService; + } + + public async Task GetLatestAsync() + { + try + { + return await _rateUploadInfoRepository.FindLatestAsync(); + } + catch (Exception exception) + { + var message = "Failure retrieving latest RateUploadInfo."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task GetByVersionAsync(int version, DateTime effectiveDate) + { + try + { + return await _rateUploadInfoRepository.FindByVersionAsync(version, effectiveDate); + } + catch (Exception exception) + { + var message = $"Failure retrieving RateUploadInfo with version ({version}) and effective date ({effectiveDate:MM/dd/yyyy})."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task GetPreviousVersion(RateUploadInfo rateUploadInfo) + { + try + { + return await _rateUploadInfoRepository.FindPreviousVersion(rateUploadInfo); + } + catch (Exception exception) + { + var message = "Failure retrieving previous RateUploadInfo. " + + $"RateUploadInfo: {JsonConvert.SerializeObject(rateUploadInfo)}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task GetLatestVersionOnDate(DateTime date) + { + try + { + return await _rateUploadInfoRepository.FindLatestVersionOnDate(date); + } + catch (Exception exception) + { + var message = $"Failure retrieving previous RateUploadInfo on date EffectiveDate: {date:MM/dd/yyyy}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task GetFilteredRateUploadInfo(RateUploadInfoFilter rateUploadInfoFilter) + { + try + { + return await _rateUploadInfoRepository.FindFilteredRateUploadInfosAsync(rateUploadInfoFilter); + } + catch (Exception exception) + { + var message = "Failure retrieving filtered RateUploadInfos."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task ComplexSave(RateUploadInfoRequest request) + { + try + { + var previousVersion = (await GetLatestVersionOnDate(request.EffectiveDate))?.Version; + + var newRates = new List(); + foreach (var newRate in request.Rates.Select(rate => rate.DeepCopy())) + { + newRate.RateId = 0; + newRate.RateStartDate = request.EffectiveDate; + newRates.Add(newRate); + } + + var oldRates = (await _rateService.GetRangeByIdAsync(request.Rates.Select(rate => rate.RateId).ToList())).ToList(); + foreach (var rate in oldRates) + { + rate.RateEndDate = request.EffectiveDate; + } + + var rateUploadInfo = new RateUploadInfo + { + Version = (previousVersion ?? 0) + 1, + Filename = request.FileName, + EffectiveDate = request.EffectiveDate, + ClosedRates = oldRates, + Rates = newRates, + RateCount = newRates.Count, + TimeCreated = DateTime.Now, + CreatedBy = request.CreatedBy + }; + + return await SaveAsync(rateUploadInfo); + } + catch (Exception exception) + { + var message = "Failure completing a complex save for RateUploadInfo: " + + $"EvaluationDate: {request.EffectiveDate:MM/dd/yyyy} | " + + $"Rate Count: {request.Rates.Count}"; + Log.Error(exception, message); + throw new ServiceException(message); + } + } + + public async Task ComplexDelete(int id) + { + try + { + if (id != (await GetLatestAsync())?.RateUploadInfoId) + { + throw new Exception("Only the most recent RateUploadInfo can be deleted."); + } + + var closedRates = (await _rateService.GetByClosingRateUploadInfoIdAsync(id))?.ToList(); + if (closedRates is not null && closedRates.Count > 0) + { + foreach (var rate in closedRates) + { + rate.RateEndDate = null; + } + + await _rateService.UpdateRangeAsync(closedRates); + } + + await DeleteAsync(id); + } + catch (Exception exception) + { + var message = $"Failure ComplexDelete for RateUploadInfo with RateUploadInfoId {id}."; + Log.Error(exception, message); + throw new ServiceException(message); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/StatusService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/StatusService.cs new file mode 100644 index 0000000..4e2c023 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/StatusService.cs @@ -0,0 +1,28 @@ +using LCU.Library.Internal.Status.Services; +using LCU.Library.Internal.StatusModels.Models; +namespace LCU.API.Internal.LoanRate.Services; + +public class StatusService : IStatusService +{ + private readonly IEnvironmentViewService _environmentViewService; + + public StatusService(IEnvironmentViewService environmentViewService) + { + _environmentViewService = environmentViewService; + } + + public EnvironmentView GetEnviromentView() + { + try + { + var environmentView = _environmentViewService.BuildEnvironmentView(); + Log.Verbose("Status Environment View Heartbeat. EnvironmentView: {@EnvironmentView}", environmentView); + return environmentView; + } + catch (Exception exception) + { + Log.Error(exception, $"Failure Getting Enviroment View Information."); + throw new ServiceException($"Failure Getting Enviroment View Information."); + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ValidateLoanRateServices/IValidateRateEvaluationsService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ValidateLoanRateServices/IValidateRateEvaluationsService.cs new file mode 100644 index 0000000..090c7f2 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ValidateLoanRateServices/IValidateRateEvaluationsService.cs @@ -0,0 +1,8 @@ +using LCU.Library.Internal.LoanRateModels.Requests; +namespace LCU.API.Internal.LoanRate.Services.ValidateLoanRateServices; + +public interface IValidateRateEvaluationsService +{ + Task ValidateRatePassesEvaluation(RateRequest request, int rateId, int evaluationDetailId); + Task ValidateRatePassesEvaluations(RateRequest request, int rateId); +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ValidateLoanRateServices/ValidateRateEvaluationsService.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ValidateLoanRateServices/ValidateRateEvaluationsService.cs new file mode 100644 index 0000000..85fc08e --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/Services/ValidateLoanRateServices/ValidateRateEvaluationsService.cs @@ -0,0 +1,58 @@ +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +using LCU.Library.Internal.LcuEvaluationModels.Enums; +using LCU.Library.Internal.LcuEvaluation.Services; +using LCU.Library.Internal.LcuEvaluation.Services.EvaluationServices; +using LCU.Library.Internal.LcuEvaluationModels.Entities; +using LCU.Library.Internal.LoanRateModels.Requests; +namespace LCU.API.Internal.LoanRate.Services.ValidateLoanRateServices; + +public class ValidateRateEvaluationsService : IValidateRateEvaluationsService +{ + private readonly IEvaluationDetailService _evaluationDetailService; + private readonly IRateService _rateService; + private readonly IRunRuleService _runRuleService; + private readonly IRateFinderService _rateFinderService; + + public ValidateRateEvaluationsService(IEvaluationDetailService evaluationDetailService, + IRateService rateService, + IRunRuleService runRuleService, + IRateFinderService rateFinderService) + { + _evaluationDetailService = evaluationDetailService; + _rateService = rateService; + _runRuleService = runRuleService; + _rateFinderService = rateFinderService; + } + + public async Task ValidateRatePassesEvaluation(RateRequest request, int rateId, int evaluationDetailId) + { + var evaluation = await _evaluationDetailService.GetByIdAsync(evaluationDetailId); + var passedEvaluations = _runRuleService.ValidateRequestRules(request, new List { evaluation }); + var rate = await _rateService.GetByIdAsync(rateId); + + var passedEvaluation = passedEvaluations.FirstOrDefault(); + if (passedEvaluation == null) + { + return false; + } + + var passRate = rate.RateEvaluations.FirstOrDefault(rateEvaluation => rateEvaluation.EvaluationDetailId == passedEvaluation.EvaluationDetailId); + return passRate != null; + } + + public async Task ValidateRatePassesEvaluations(RateRequest request, int rateId) + { + var rate = await _rateService.GetByIdAsync(rateId); + var evaluationIds = rate.RateEvaluations.Select(rateEvaluation => rateEvaluation.EvaluationDetailId).ToList(); + var evaluations = await _evaluationDetailService.GetActiveByEvaluationTypeAndIdsAsync(request.ApplicationDate, EvaluationTypes.Rule, evaluationIds); + if (!evaluations.Any()) + { + return false; + } + + var passedEvaluations = _runRuleService.ValidateRequestRules(request, evaluations.ToList()); + var matchingRate = (await _rateFinderService.DetermineRate(request.ApplicationDate, passedEvaluations)).rate; + + return matchingRate != null && matchingRate.RateId == rateId; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/appsettings.json b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/appsettings.json new file mode 100644 index 0000000..4049327 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/appsettings.json @@ -0,0 +1,73 @@ +{ + "Primary": { + "AppName": "Loan Rate API", + "SeqUrl": "https://qa-seq.lcu-internal.com", + "SeqApiKey": "", + "CorsAllowedOrigins": [ + "http://localhost:3000", + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://qa-api.lcu-internal.com" + ], + "FeatureFlags": { + "RunLoadTestInitiateConsumer": true, + "RunLoadTestProcessRateConsumer": true, + "RunLoadTestRateCompletionConsumer": true + } + }, + "LoanRateSqlConnectionConfiguration": { + "ConnectionString": "data source=localhost;initial catalog=LoanRate;Integrated Security=SSPI;MultipleActiveResultSets=True;App=EntityFramework;Encrypt=False;", + "MaxRetryCount": 3, + "MaxRetryDelay": "00:00:30" + }, + "LoanRateLoadTestingSqlConnectionConfiguration": { + "ConnectionString": "data source=localhost;initial catalog=LoanRateLoadTest;Integrated Security=SSPI;MultipleActiveResultSets=True;App=EntityFramework;Encrypt=False;", + "MaxRetryCount": 3, + "MaxRetryDelay": "00:00:30" + }, + "EvaluationSqlConnectionConfiguration": { + "ConnectionString": "data source=localhost;initial catalog=LoanRate;Integrated Security=SSPI;MultipleActiveResultSets=True;App=EntityFramework;Encrypt=False;", + "MaxRetryCount": 3, + "MaxRetryDelay": "00:00:30" + }, + "ConnectionConfiguration": { + "HostUrl": "localhost", + "UserName": "guest", + "Password": "guest" + }, + "LoadTestInitiateReceiveEndpoint": { + "ReceiveEndpoint": "LCU.LoanRate.LoadTest.Initiate", + "ReceiveEndpointType": "exchange", + "PrefetchLimit": 1 + }, + "LoadTestProcessRateReceiveEndpoint": { + "ReceiveEndpoint": "LCU.LoanRate.LoadTest.ProcessRate", + "ReceiveEndpointType": "exchange", + "PrefetchLimit": 1 + }, + "LoadTestRateCompletionReceiveEndpoint": { + "ReceiveEndpoint": "LCU.LoanRate.LoadTest.RateCompletion", + "ReceiveEndpointType": "exchange", + "PrefetchLimit": 100 + }, + "FileOperationsApiConfiguration": { + "Url": "https://localhost:44368", + "ExtractExcelWorkBookDataEndpoint": "/api/Excel/ExtractExcelWorkBookData" + }, + "RateCacheConfiguration": { + "CacheName": "RateCache", + "ExpirationMinutes": "480" + }, + "RateUploadInfoCacheConfiguration": { + "CacheName": "RateUploadInfoCache", + "ExpirationMinutes": "480" + }, + "RateSheetCacheConfiguration": { + "CacheName": "RateSheetCache", + "ExpirationMinutes": "480" + }, + "EvaluationCacheConfiguration": { + "CacheName": "EvaluationCache", + "ExpirationMinutes": "480" + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/using.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/using.cs new file mode 100644 index 0000000..0366b69 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRate/using.cs @@ -0,0 +1,3 @@ +global using Serilog; +global using LCU.Library.Internal.Exceptions; +global using LCU.Library.Internal.LoanRateModels.Entities; \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Configurations/RateCacheConfiguration.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Configurations/RateCacheConfiguration.cs new file mode 100644 index 0000000..ad840f5 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Configurations/RateCacheConfiguration.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LcuCaching.Configurations; + +namespace LCU.API.Internal.LoanRateLib.Configurations; + +public class RateCacheConfiguration : CacheConfiguration +{ +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Configurations/RateSheetCacheConfiguration.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Configurations/RateSheetCacheConfiguration.cs new file mode 100644 index 0000000..ebf4d4f --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Configurations/RateSheetCacheConfiguration.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LcuCaching.Configurations; + +namespace LCU.API.Internal.LoanRateLib.Configurations; + +public class RateSheetCacheConfiguration : CacheConfiguration +{ +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Configurations/RateUploadInfoCacheConfiguration.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Configurations/RateUploadInfoCacheConfiguration.cs new file mode 100644 index 0000000..a3fb9ec --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Configurations/RateUploadInfoCacheConfiguration.cs @@ -0,0 +1,7 @@ +namespace LCU.API.Internal.LoanRateLib.Configurations; + +public class RateUploadInfoCacheConfiguration +{ + public string CacheName { get; set; } + public int ExpirationMinutes { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/IncludeAllQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/IncludeAllQueryable.cs new file mode 100644 index 0000000..116265e --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/IncludeAllQueryable.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore; +namespace LCU.API.Internal.LoanRateLib.Extensions.Queryables; + +public static class IncludeAllQueryable +{ + public static IQueryable IncludeAll(this IQueryable queryable) + { + return queryable.Include(rate => rate.RateEvaluations); + } + + public static IQueryable IncludeAllRatesEvaluations(this IQueryable queryable) + { + return queryable.Include(rate => rate.RateEvaluations) + .ThenInclude(rate => rate.EvaluationDetail); + } + + public static IQueryable IncludeAll(this IQueryable queryable) + { + return queryable.Include(rateUploadInfo => rateUploadInfo.Rates) + .Include(rateUploadInfo => rateUploadInfo.ClosedRates) + .ThenInclude(rate => rate.RateEvaluations); + } + + public static IQueryable IncludeAllButClosedRates(this IQueryable queryable) + { + return queryable.Include(rateUploadInfo => rateUploadInfo.Rates) + .ThenInclude(rate => rate.RateEvaluations); + } + + public static IQueryable IncludeAll(this IQueryable queryable) + { + return queryable.Include(rateSheet => rateSheet.RateSheetCells) + .Include(rateSheet => rateSheet.Rates) + .ThenInclude(rate => rate.RateEvaluations); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/IncludeFiltersQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/IncludeFiltersQueryable.cs new file mode 100644 index 0000000..2ca5f5b --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/IncludeFiltersQueryable.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.LoanRateLib.Extensions.Queryables; +public static class IncludeFiltersQueryable +{ + public static IQueryable IncludeFilters(this IQueryable queryable, RateUploadInfoFilter rateUploadInfoFilter) + => queryable.FilterByEffectiveDate(rateUploadInfoFilter) + .FilterByVersion(rateUploadInfoFilter) + .FilterByCreatedBy(rateUploadInfoFilter); +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/RateUploadInfoCreatedByQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/RateUploadInfoCreatedByQueryable.cs new file mode 100644 index 0000000..0ac318a --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/RateUploadInfoCreatedByQueryable.cs @@ -0,0 +1,13 @@ +namespace LCU.API.Internal.LoanRateLib.Extensions.Queryables; +public static class RateUploadInfoCreatedByQueryable +{ + public static IQueryable FilterByCreatedBy(this IQueryable queryable, RateUploadInfoFilter rateUploadInfoFilter) + { + if (rateUploadInfoFilter.CreatedBy == null) + { + return queryable; + } + + return queryable.Where(rateUploadInfo => rateUploadInfo.CreatedBy.ToUpper().Contains(rateUploadInfoFilter.CreatedBy.ToUpper())); + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/RateUploadInfoEffectiveDateQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/RateUploadInfoEffectiveDateQueryable.cs new file mode 100644 index 0000000..a8dfff7 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/RateUploadInfoEffectiveDateQueryable.cs @@ -0,0 +1,13 @@ +namespace LCU.API.Internal.LoanRateLib.Extensions.Queryables; +public static class RateUploadInfoEffectiveDateQueryable +{ + public static IQueryable FilterByEffectiveDate(this IQueryable queryable, RateUploadInfoFilter rateUploadInfoFilter) + { + if (rateUploadInfoFilter.EffectiveDate == null) + { + return queryable; + } + + return queryable.Where(rateUploadInfo => rateUploadInfo.EffectiveDate.Date == ((DateTime)rateUploadInfoFilter.EffectiveDate).Date); + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/RateUploadInfoVersionQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/RateUploadInfoVersionQueryable.cs new file mode 100644 index 0000000..5c6b6db --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/RateUploadInfoVersionQueryable.cs @@ -0,0 +1,13 @@ +namespace LCU.API.Internal.LoanRateLib.Extensions.Queryables; +public static class RateUploadInfoVersionQueryable +{ + public static IQueryable FilterByVersion(this IQueryable queryable, RateUploadInfoFilter rateUploadInfoFilter) + { + if (rateUploadInfoFilter.Version == null) + { + return queryable; + } + + return queryable.Where(rateUploadInfo => rateUploadInfo.Version == (int)rateUploadInfoFilter.Version); + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/SortableFieldsQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/SortableFieldsQueryable.cs new file mode 100644 index 0000000..6d88030 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Extensions/Queryables/SortableFieldsQueryable.cs @@ -0,0 +1,28 @@ +using System.Linq.Dynamic.Core; +using System.Text; + +namespace LCU.API.Internal.LoanRateLib.Extensions.Queryables; +public static class SortableFieldsQueryable +{ + public static IQueryable OrderBySortableFields(this IQueryable queryable, RateUploadInfoFilter rateUploadInfoFilter) + { + var sortableFields = rateUploadInfoFilter.SortableFields + .Where(sortableField => sortableField.Active && sortableField.Position >= 0) + .OrderBy(sortableField => sortableField.Position).ToList(); + + if (sortableFields == null || !sortableFields.Any()) + { + return queryable; + } + + var orderBy = new StringBuilder(); + foreach (var field in sortableFields) + { + var addedComma = orderBy.ToString() == "" ? "" : " ,"; + var desc = field.IsDescending ? " desc" : ""; + orderBy.Append($"{addedComma}{field.Field}{desc}"); + } + + return queryable.OrderBy(orderBy.ToString()); + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/LCU.API.Internal.LoanRateLib.csproj b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/LCU.API.Internal.LoanRateLib.csproj new file mode 100644 index 0000000..095e936 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/LCU.API.Internal.LoanRateLib.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + + + + + + + + + + + + + + + + diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Persistence/AppDbContext.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Persistence/AppDbContext.cs new file mode 100644 index 0000000..9ec3fca --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Persistence/AppDbContext.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore; +namespace LCU.API.Internal.LoanRateLib.Persistence; + +public class AppDbContext : DbContext +{ + public DbSet Rates { get; set; } + public DbSet RateEvaluations { get; set; } + public DbSet RateUploadInfos { get; set; } + public DbSet RateSheets { get; set; } + public DbSet RateSheetCells { get; set; } + public AppDbContext(DbContextOptions options) : base(options) { } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.Entity().ToTable("Rate"); + builder.Entity().ToTable("RateEvaluation"); + builder.Entity().ToTable("RateUploadInfo") + .HasMany(rateUploadInfo => rateUploadInfo.Rates) + .WithOne() + .HasForeignKey(rate => rate.RateUploadInfoId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Cascade); + builder.Entity().ToTable("RateUploadInfo") + .HasMany(rateUploadInfo => rateUploadInfo.ClosedRates) + .WithOne() + .HasForeignKey(rate => rate.ClosingRateUploadInfoId) + .IsRequired(false) + .OnDelete(DeleteBehavior.SetNull); + builder.Entity().ToTable("RateSheet") + .HasMany(rateSheet => rateSheet.Rates) + .WithOne() + .HasForeignKey(rate => rate.RateSheetId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Cascade); + builder.Entity().ToTable("RateSheet") + .HasMany(rateSheet => rateSheet.RateSheetCells) + .WithOne() + .HasForeignKey(rateSheetCell => rateSheetCell.RateSheetId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Cascade); + builder.Entity().ToTable("RateSheetCell"); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/BaseRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/BaseRepository.cs new file mode 100644 index 0000000..a18cbd0 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/BaseRepository.cs @@ -0,0 +1,12 @@ +using LCU.API.Internal.LoanRateLib.Persistence; +namespace LCU.API.Internal.LoanRateLib.Repositories; + +public abstract class BaseRepository +{ + protected readonly AppDbContext _context; + + public BaseRepository(AppDbContext context) + { + _context = context; + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateEvaluationRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateEvaluationRepository.cs new file mode 100644 index 0000000..418c528 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateEvaluationRepository.cs @@ -0,0 +1,18 @@ +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.API.Internal.LoanRateLib.Repositories; + +public interface IRateEvaluationRepository +{ + Task?> FindAsync(); + Task FindByIdAsync(int id); + Task?> FindRangeByIdAsync(List ids); + Task?> FindRangeByEvaluationDetailIdAsync(int evaluationDetailId); + Task?> FindByRateSheetIdsAsync(List rateSheetIds); + Task AddAsync(RateEvaluation rateEvaluation); + Task AddRangeAsync(IEnumerable rateEvaluations); + void UpdateRange(IEnumerable rateEvaluations); + void Remove(RateEvaluation rateEvaluation); + void RemoveRange(IEnumerable rateEvaluations); + Task CountByEvaluationDetailIdAsync(int evaluationDetailId); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateRepository.cs new file mode 100644 index 0000000..fac0899 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateRepository.cs @@ -0,0 +1,23 @@ +using LCU.Library.Internal.LcuEvaluationModels.Enums; + +namespace LCU.API.Internal.LoanRateLib.Repositories; +public interface IRateRepository +{ + Task?> FindAsync(); + Task?> FindWithRateEvaluationsAsync(); + Task FindByIdAsync(int id); + Task?> FindByClosingRateUploadInfoIdAsync(int id); + Task?> FindByDateAsync(DateTime date); + Task?> FindRateHistoryAsync(Rate rate); + Task?> FindRangeByIdAsync(List ids); + Task?> FindByDateEvaluationIdsAndEvaluationTypeAsync(DateTime date, + List evaluationIds, + EvaluationTypes evaluationType); + Task AddAsync(Rate rate); + Task AddRangeAsync(IEnumerable rates); + void UpdateRange(IEnumerable rates); + void Remove(Rate rate); + void RemoveRange(IEnumerable rates); + Task> FindCacheAsync(); + void ClearCache(); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateSheetCellRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateSheetCellRepository.cs new file mode 100644 index 0000000..e29a854 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateSheetCellRepository.cs @@ -0,0 +1,6 @@ +using LCU.Library.Internal.LcuClassPatterns.Crud.Repositories; + +namespace LCU.API.Internal.LoanRateLib.Repositories; +public interface IRateSheetCellRepository : ICrudRepository +{ +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateSheetRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateSheetRepository.cs new file mode 100644 index 0000000..a47e3d2 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateSheetRepository.cs @@ -0,0 +1,15 @@ +using LCU.Library.Internal.LcuClassPatterns.Crud.Repositories; +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.API.Internal.LoanRateLib.Repositories; +public interface IRateSheetRepository : ICrudRepository +{ + Task> FindIncludeAllAsync(); + Task> FindIncludeRateSheetCellsAsync(); + Task?> FindRatesByDateAndRateType(DateTime date, RateTypes rateType); + Task FindByDateAndRateType(DateTime date, RateTypes rateType); + Task?> FindRatesByRateType(RateTypes rateType); + Task?> FindRateSheetIdsByRateType(RateTypes rateType); + Task> FindCacheAsync(); + void ClearCache(); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateUploadInfoRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateUploadInfoRepository.cs new file mode 100644 index 0000000..62f5180 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IRateUploadInfoRepository.cs @@ -0,0 +1,12 @@ +using LCU.Library.Internal.LcuClassPatterns.Crud.Repositories; +using LCU.Library.Internal.LoanRateModels.Responses; + +namespace LCU.API.Internal.LoanRateLib.Repositories; +public interface IRateUploadInfoRepository : ICrudRepository +{ + Task FindLatestAsync(); + Task FindByVersionAsync(int version, DateTime effectiveDate); + Task FindPreviousVersion(RateUploadInfo rateUploadInfo); + Task FindLatestVersionOnDate(DateTime effectiveDate); + Task FindFilteredRateUploadInfosAsync(RateUploadInfoFilter rateUploadInfoFilter); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IUnitOfWork.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IUnitOfWork.cs new file mode 100644 index 0000000..8b73918 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/IUnitOfWork.cs @@ -0,0 +1,7 @@ +namespace LCU.API.Internal.LoanRateLib.Repositories; + +public interface IUnitOfWork +{ + Task CompleteAsync(); + void Complete(); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateEvaluationRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateEvaluationRepository.cs new file mode 100644 index 0000000..b214073 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateEvaluationRepository.cs @@ -0,0 +1,81 @@ +using Microsoft.EntityFrameworkCore; +using LCU.API.Internal.LoanRateLib.Persistence; +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.API.Internal.LoanRateLib.Repositories; + +public class RateEvaluationRepository : BaseRepository, IRateEvaluationRepository +{ + public IRateRepository _rateRepository; + + public RateEvaluationRepository(AppDbContext context, + IRateRepository rateRepository) : base(context) + { + _rateRepository = rateRepository; + } + + public async Task?> FindAsync() + { + return await _context.RateEvaluations.ToListAsync(); + } + + public async Task FindByIdAsync(int id) + { + return await _context.RateEvaluations.FirstOrDefaultAsync(rateEvaluation => rateEvaluation.RateEvaluationId == id); + } + + public async Task?> FindRangeByIdAsync(List ids) + { + return await _context.RateEvaluations + .Where(rateEvaluation => ids.Contains(rateEvaluation.RateEvaluationId)) + .ToListAsync(); + } + + public async Task?> FindRangeByEvaluationDetailIdAsync(int evaluationDetailId) + { + return await _context.RateEvaluations + .Where(rateEvaluation => rateEvaluation.EvaluationDetailId == evaluationDetailId) + .ToListAsync(); + } + + public async Task?> FindByRateSheetIdsAsync(List rateSheetIds) + { + return (await _rateRepository.FindCacheAsync()) + .Where(rate => rateSheetIds.Contains(rate.RateSheetId)) + .SelectMany(rate => rate.RateEvaluations); + } + + public async Task AddAsync(RateEvaluation rateEvaluation) + { + return (await _context.RateEvaluations.AddAsync(rateEvaluation)).Entity; + } + + public async Task AddRangeAsync(IEnumerable rateEvaluations) + { + await _context.RateEvaluations.AddRangeAsync(rateEvaluations); + } + + public void UpdateRange(IEnumerable rateEvaluations) + { + _context.RateEvaluations.UpdateRange(rateEvaluations); + } + + public void Remove(RateEvaluation rateEvaluation) + { + _context.RateEvaluations.Remove(rateEvaluation); + } + + public void RemoveRange(IEnumerable rateEvaluations) + { + _context.RateEvaluations.RemoveRange(rateEvaluations); + } + + public async Task CountByEvaluationDetailIdAsync(int evaluationDetailId) + { + return await _context.RateEvaluations + .Where(rateEvaluation => rateEvaluation.EvaluationDetailId == evaluationDetailId) + .Select(rateEvaluation => rateEvaluation.RateId) + .Distinct() + .CountAsync(); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateRepository.cs new file mode 100644 index 0000000..ac59868 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateRepository.cs @@ -0,0 +1,139 @@ +using Microsoft.EntityFrameworkCore; +using LCU.API.Internal.LoanRateLib.Configurations; +using LCU.API.Internal.LoanRateLib.Extensions.Queryables; +using LCU.API.Internal.LoanRateLib.Persistence; +using LCU.Library.Internal.LcuCaching.Services; +using LCU.Library.Internal.LcuEvaluationModels.Enums; + +namespace LCU.API.Internal.LoanRateLib.Repositories; + +public class RateRepository : BaseRepository, IRateRepository +{ + private readonly RateCacheConfiguration _rateCacheConfiguration; + private readonly ICacheService _cacheService; + + public RateRepository(AppDbContext context, + RateCacheConfiguration rateCacheConfiguration, + ICacheService cacheService) : base(context) + { + _rateCacheConfiguration = rateCacheConfiguration; + _cacheService = cacheService; + } + + public async Task?> FindAsync() + { + return await _context.Rates.IncludeAll().ToListAsync(); + } + + public async Task?> FindWithRateEvaluationsAsync() + { + return await _context.Rates.IncludeAllRatesEvaluations().ToListAsync(); + } + + public async Task FindByIdAsync(int id) + { + return await _context.Rates.IncludeAll().FirstOrDefaultAsync(rate => rate.RateId == id); + } + + public async Task?> FindByClosingRateUploadInfoIdAsync(int id) + { + return await _context.Rates.Where(rate => rate.ClosingRateUploadInfoId == id) + .ToListAsync(); + } + + public async Task?> FindByDateAsync(DateTime date) + { + return await _context.Rates.Where(rate => date >= rate.RateStartDate && + (rate.RateEndDate == null || date < rate.RateEndDate)) + .IncludeAll() + .ToListAsync(); + } + + public async Task?> FindRateHistoryAsync(Rate rate) + { + return await _context.Rates.Where(x => x.Row == rate.Row + && x.Column == rate.Column + && x.RateSheetId == rate.RateSheetId) + .ToListAsync(); + } + + public async Task?> FindRangeByIdAsync(List ids) + { + return await _context.Rates + .Where(rate => ids.Contains(rate.RateId)) + .IncludeAll() + .ToListAsync(); + } + + /// + /// Will get Rates that: + /// - Are between the RateStartDate-RateEndDate(if not null). + /// - Match the evaluation type + /// - Match All the EvaluationIds + /// - Contains ALL the evaluationids and the count of RateEvaluation.EvaluationDetailIds match the count of evaluationIds + /// + /// + /// + /// + /// + public async Task?> FindByDateEvaluationIdsAndEvaluationTypeAsync(DateTime date, + List evaluationIds, + EvaluationTypes evaluationType) + { + return await _context.Rates + .Where( + rate => date >= rate.RateStartDate && + (!rate.RateEndDate.HasValue || date < rate.RateEndDate) && + (rate.RateEvaluations.Any() && + rate.RateEvaluations + .Where(rateEvaluation => + (date >= rateEvaluation.EvaluationDetail.StartDate && + (!rateEvaluation.EvaluationDetail.EndDate.HasValue || date < rateEvaluation.EvaluationDetail.EndDate)) && + rateEvaluation.EvaluationDetail.EvaluationType == evaluationType && + rateEvaluation.EvaluationDetail.Active == true) + .All(rateEvaluation => evaluationIds.Contains(rateEvaluation.EvaluationDetailId)) && + rate.RateEvaluations + .Where(rateEvaluation => + (date >= rateEvaluation.EvaluationDetail.StartDate && + (!rateEvaluation.EvaluationDetail.EndDate.HasValue || date < rateEvaluation.EvaluationDetail.EndDate)) && + rateEvaluation.EvaluationDetail.EvaluationType == evaluationType && + rateEvaluation.EvaluationDetail.Active == true && + evaluationIds.Contains(rateEvaluation.EvaluationDetailId)) + .Select(rateEvaluation => rateEvaluation.EvaluationDetailId).Count() == evaluationIds.Count)) + .IncludeAllRatesEvaluations() + .ToListAsync(); + } + + public async Task AddAsync(Rate rate) + { + return (await _context.Rates.AddAsync(rate)).Entity; + } + + public async Task AddRangeAsync(IEnumerable rates) + { + await _context.Rates.AddRangeAsync(rates); + } + + public void UpdateRange(IEnumerable rates) + { + _context.Rates.UpdateRange(rates); + } + + public void Remove(Rate rate) + { + _context.Rates.Remove(rate); + } + + public void RemoveRange(IEnumerable rates) + { + _context.Rates.RemoveRange(rates); + } + + public async Task> FindCacheAsync() + => await _cacheService.FindCacheAsync(FindAsync, + _rateCacheConfiguration.CacheName, + _rateCacheConfiguration.ExpirationMinutes); + + public void ClearCache() + => _cacheService.ClearCache(_rateCacheConfiguration.CacheName); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateSheetCellRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateSheetCellRepository.cs new file mode 100644 index 0000000..15bda4a --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateSheetCellRepository.cs @@ -0,0 +1,13 @@ +using LCU.API.Internal.LoanRateLib.Persistence; +using LCU.Library.Internal.LcuClassPatterns.Crud.Repositories; + +namespace LCU.API.Internal.LoanRateLib.Repositories; +public class RateSheetCellRepository : CrudRepository, IRateSheetCellRepository +{ + private readonly AppDbContext _context; + + public RateSheetCellRepository(AppDbContext context) : base(context) + { + _context = context; + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateSheetRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateSheetRepository.cs new file mode 100644 index 0000000..1228458 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateSheetRepository.cs @@ -0,0 +1,83 @@ +using LCU.API.Internal.LoanRateLib.Configurations; +using LCU.API.Internal.LoanRateLib.Extensions.Queryables; +using LCU.API.Internal.LoanRateLib.Persistence; +using LCU.Library.Internal.LcuCaching.Services; +using LCU.Library.Internal.LcuClassPatterns.Crud.Repositories; +using LCU.Library.Internal.LoanRateModels.Enums; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.LoanRateLib.Repositories; +public class RateSheetRepository : CrudRepository, IRateSheetRepository +{ + private readonly AppDbContext _context; + private readonly RateSheetCacheConfiguration _rateSheetCacheConfiguration; + private readonly ICacheService _cacheService; + + public RateSheetRepository(AppDbContext context, + RateSheetCacheConfiguration rateSheetCacheConfiguration, + ICacheService cacheService) : base(context) + { + _context = context; + _rateSheetCacheConfiguration = rateSheetCacheConfiguration; + _cacheService = cacheService; + } + + public async Task> FindIncludeAllAsync() + { + return await _context.RateSheets.IncludeAll().ToListAsync(); + } + + public async Task> FindIncludeRateSheetCellsAsync() + { + return await _context.RateSheets.Include(rateSheet => rateSheet.RateSheetCells).ToListAsync(); + } + + public async Task?> FindRatesByDateAndRateType(DateTime date, RateTypes rateType) + { + return (await FindCacheAsync()).FirstOrDefault(rateSheet => rateSheet.RateType == rateType)? + .Rates.Where(rate => + date >= rate.RateStartDate + && (rate.RateEndDate == null || date < rate.RateEndDate)); + } + + public async Task FindByDateAndRateType(DateTime date, RateTypes rateType) + { + return await _context.RateSheets.Include(rateSheet => rateSheet.Rates) + .Include(rateSheet => rateSheet.RateSheetCells) + .Where(rateSheet => rateSheet.RateType == rateType) + .Select(rateSheet => + new RateSheet + { + RateSheetId = rateSheet.RateSheetId, + RateType = rateSheet.RateType, + RateSheetCells = rateSheet.RateSheetCells, + Rates = _context.Rates.Where(rate => rate.RateSheetId == rateSheet.RateSheetId + && date >= rate.RateStartDate + && (rate.RateEndDate == null || date < rate.RateEndDate)).ToList(), + CreatedBy = rateSheet.CreatedBy, + TimeCreated = rateSheet.TimeCreated + }) + .FirstOrDefaultAsync(); + } + + public async Task?> FindRatesByRateType(RateTypes rateType) + { + return (await FindCacheAsync()).FirstOrDefault(rateSheet => rateSheet.RateType == rateType)?.Rates; + } + + public async Task?> FindRateSheetIdsByRateType(RateTypes rateType) + { + return await _context.RateSheets.Where(rateSheet => rateSheet.RateType == rateType) + .Select(rateSheet => rateSheet.RateSheetId) + .ToListAsync(); + } + + public async Task> FindCacheAsync() + { + return await _cacheService.FindCacheAsync(FindIncludeAllAsync, + _rateSheetCacheConfiguration.CacheName, + _rateSheetCacheConfiguration.ExpirationMinutes); + } + + public void ClearCache() => _cacheService.ClearCache(_rateSheetCacheConfiguration.CacheName); +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateUploadInfoRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateUploadInfoRepository.cs new file mode 100644 index 0000000..27a553c --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/RateUploadInfoRepository.cs @@ -0,0 +1,91 @@ +using LCU.API.Internal.LoanRateLib.Extensions.Queryables; +using LCU.API.Internal.LoanRateLib.Persistence; +using LCU.Library.Internal.LcuClassPatterns.Crud.Repositories; +using LCU.Library.Internal.LoanRateModels.Responses; +using Microsoft.EntityFrameworkCore; + +namespace LCU.API.Internal.LoanRateLib.Repositories; +public class RateUploadInfoRepository : CrudRepository, IRateUploadInfoRepository +{ + private readonly AppDbContext _context; + + public RateUploadInfoRepository(AppDbContext context) : base(context) + { + _context = context; + } + + public async Task FindLatestAsync() + { + return await _context.RateUploadInfos.OrderByDescending(r => r.EffectiveDate) + .ThenByDescending(r => r.Version) + .FirstOrDefaultAsync(); + } + + public async Task FindByVersionAsync(int version, DateTime effectiveDate) + { + return await _context.RateUploadInfos.Where(rateUploadInfo => rateUploadInfo.EffectiveDate.Date == effectiveDate.Date + && rateUploadInfo.Version == version) + .IncludeAllButClosedRates() + .FirstOrDefaultAsync(); + } + + public async Task FindPreviousVersion(RateUploadInfo rateUploadInfo) + { + return await _context.RateUploadInfos.Where(r => r.EffectiveDate.Date <= rateUploadInfo.EffectiveDate.Date + && (r.EffectiveDate.Date != rateUploadInfo.EffectiveDate.Date || r.Version < rateUploadInfo.Version ) + && r.RateUploadInfoId != rateUploadInfo.RateUploadInfoId) + .OrderByDescending(r => r.EffectiveDate) + .ThenByDescending(r => r.Version) + .IncludeAllButClosedRates() + .FirstOrDefaultAsync(); + } + + public async Task FindLatestVersionOnDate(DateTime effectiveDate) + { + return await _context.RateUploadInfos.Where(r => r.EffectiveDate.Date == effectiveDate.Date) + .OrderByDescending(r => r.Version) + .IncludeAllButClosedRates() + .FirstOrDefaultAsync(); + } + + public override async Task> FindAsync() + { + return await _context.RateUploadInfos.IncludeAllButClosedRates().ToListAsync(); + } + + public async Task FindFilteredRateUploadInfosAsync(RateUploadInfoFilter rateUploadInfoFilter) + { + var results = new RateUploadInfoFilterResult(); + var skip = (rateUploadInfoFilter.Page - 1) * rateUploadInfoFilter.Limit; + + results.Total = await _context.RateUploadInfos + .IncludeFilters(rateUploadInfoFilter) + .CountAsync(); + + results.Items = await _context.RateUploadInfos + .IncludeFilters(rateUploadInfoFilter) + .OrderBySortableFields(rateUploadInfoFilter) + .Skip(skip) + .Take(rateUploadInfoFilter.Limit) + .ToArrayAsync(); + + results.TotalCount = await _context.RateUploadInfos.CountAsync(); + results.LatestRateUploadInfo = await FindLatestAsync(); + + return results; + } + + public override async Task FindByIdAsync(int id) + { + return await _context.RateUploadInfos.Where(rateUploadInfo => rateUploadInfo.RateUploadInfoId == id) + .IncludeAllButClosedRates() + .FirstOrDefaultAsync(); + } + + public override async Task> FindRangeByIdAsync(List ids) + { + return await _context.RateUploadInfos.Where(rateUploadInfo => ids.Contains(rateUploadInfo.RateUploadInfoId)) + .IncludeAllButClosedRates() + .ToListAsync(); + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/UnitOfWork.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/UnitOfWork.cs new file mode 100644 index 0000000..2c116a0 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/Repositories/UnitOfWork.cs @@ -0,0 +1,22 @@ +using LCU.API.Internal.LoanRateLib.Persistence; +namespace LCU.API.Internal.LoanRateLib.Repositories; + +public class UnitOfWork : IUnitOfWork +{ + private readonly AppDbContext _context; + + public UnitOfWork(AppDbContext context) + { + _context = context; + } + + public async Task CompleteAsync() + { + await _context.SaveChangesAsync(); + } + + public void Complete() + { + _context.SaveChanges(); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/using.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/using.cs new file mode 100644 index 0000000..b32a29d --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLib/using.cs @@ -0,0 +1 @@ +global using LCU.Library.Internal.LoanRateModels.Entities; \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/IncludeAllQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/IncludeAllQueryable.cs new file mode 100644 index 0000000..af1317f --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/IncludeAllQueryable.cs @@ -0,0 +1,9 @@ +namespace LCU.API.Internal.LoanRateLoadTestingLib.Extensions.Queryables; + +public static class IncludeAllQueryable +{ + public static IQueryable IncludeAll(this IQueryable queryable) + { + return queryable.Include(loadTestRunInfo => loadTestRunInfo.RateProcessingInfos); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/IncludeFiltersQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/IncludeFiltersQueryable.cs new file mode 100644 index 0000000..ae564a4 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/IncludeFiltersQueryable.cs @@ -0,0 +1,8 @@ +namespace LCU.API.Internal.LoanRateLoadTestingLib.Extensions.Queryables; +public static class IncludeFiltersQueryable +{ + public static IQueryable IncludeFilters(this IQueryable queryable, LoadTestRunInfoFilter loadTestRunInfoFilter) + => queryable.FilterRateType(loadTestRunInfoFilter) + .FilterInProgress(loadTestRunInfoFilter) + .FilterByRunTime(loadTestRunInfoFilter); +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/LoadTestRunInfoInProgressQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/LoadTestRunInfoInProgressQueryable.cs new file mode 100644 index 0000000..fdbdd41 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/LoadTestRunInfoInProgressQueryable.cs @@ -0,0 +1,13 @@ +namespace LCU.API.Internal.LoanRateLoadTestingLib.Extensions.Queryables; +public static class LoadTestRunInfoInProgressQueryable +{ + public static IQueryable FilterInProgress(this IQueryable queryable, LoadTestRunInfoFilter loadTestRunInfoFilter) + { + if (!loadTestRunInfoFilter.InProgress) + { + return queryable; + } + + return queryable.Where(loadTestRunInfo => !loadTestRunInfo.EndTimeStamp.HasValue); + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/LoadTestRunInfoRateTypeQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/LoadTestRunInfoRateTypeQueryable.cs new file mode 100644 index 0000000..f8dcc6c --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/LoadTestRunInfoRateTypeQueryable.cs @@ -0,0 +1,13 @@ +namespace LCU.API.Internal.LoanRateLoadTestingLib.Extensions.Queryables; +public static class LoadTestRunInfoRateTypeQueryable +{ + public static IQueryable FilterRateType(this IQueryable queryable, LoadTestRunInfoFilter loadTestRunInfoFilter) + { + if (loadTestRunInfoFilter?.RateTypes == null || !loadTestRunInfoFilter.RateTypes.Any()) + { + return queryable; + } + + return queryable.Where(loadTestRunInfo => loadTestRunInfoFilter.RateTypes.Contains(loadTestRunInfo.RateType)); + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/LoadTestRunInfoRunTimeQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/LoadTestRunInfoRunTimeQueryable.cs new file mode 100644 index 0000000..f9d6145 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/LoadTestRunInfoRunTimeQueryable.cs @@ -0,0 +1,21 @@ +namespace LCU.API.Internal.LoanRateLoadTestingLib.Extensions.Queryables; +public static class LoadTestRunInfoRunTimeQueryable +{ + public static IQueryable FilterByRunTime(this IQueryable queryable, LoadTestRunInfoFilter loadTestRunInfoFilter) + { + if (loadTestRunInfoFilter.StartTimeStamp == null && loadTestRunInfoFilter.EndTimeStamp == null) + { + return queryable; + } + + if (loadTestRunInfoFilter.StartTimeStamp == null) + { + loadTestRunInfoFilter.StartTimeStamp = DateTime.MinValue; + } + + return queryable.Where(loadTestRunInfo => loadTestRunInfo.StartTimeStamp >= loadTestRunInfoFilter.StartTimeStamp && + (loadTestRunInfoFilter.InProgress + || !loadTestRunInfoFilter.EndTimeStamp.HasValue + || loadTestRunInfo.StartTimeStamp <= loadTestRunInfoFilter.EndTimeStamp)); + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/SortableFieldsQueryable.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/SortableFieldsQueryable.cs new file mode 100644 index 0000000..c121ed2 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Extensions/Queryables/SortableFieldsQueryable.cs @@ -0,0 +1,28 @@ +using System.Text; +using System.Linq.Dynamic.Core; + +namespace LCU.API.Internal.LoanRateLoadTestingLib.Extensions.Queryables; +public static class SortableFieldsQueryable +{ + public static IQueryable OrderBySortableFields(this IQueryable queryable, LoadTestRunInfoFilter loadTestRunInfoFilter) + { + var sortableFields = loadTestRunInfoFilter.SortableFields + .Where(sortableField => sortableField.Active && sortableField.Position >= 0) + .OrderBy(sortableField => sortableField.Position).ToList(); + + if (sortableFields == null || !sortableFields.Any()) + { + return queryable; + } + + var orderBy = new StringBuilder(); + foreach (var field in sortableFields) + { + var addedComma = orderBy.ToString() == "" ? "" : " ,"; + var desc = field.IsDescending ? " desc" : ""; + orderBy.Append($"{addedComma}{field.Field}{desc}"); + } + + return queryable.OrderBy(orderBy.ToString()); + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/LCU.API.Internal.LoanRateLoadTestingLib.csproj b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/LCU.API.Internal.LoanRateLoadTestingLib.csproj new file mode 100644 index 0000000..d3e13d7 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/LCU.API.Internal.LoanRateLoadTestingLib.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Persistence/AppDbContext.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Persistence/AppDbContext.cs new file mode 100644 index 0000000..b3680fc --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Persistence/AppDbContext.cs @@ -0,0 +1,16 @@ +namespace LCU.API.Internal.LoanRateLoadTestingLib.Persistence; + +public class AppDbContext : DbContext +{ + public DbSet LoadTestRunInfos { get; set; } + public DbSet RateProcessingInfos { get; set; } + public AppDbContext(DbContextOptions options) : base(options) { } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.Entity().ToTable("LoadTestRunInfo"); + builder.Entity().ToTable("RateProcessingInfo"); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/BaseRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/BaseRepository.cs new file mode 100644 index 0000000..2c7952b --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/BaseRepository.cs @@ -0,0 +1,12 @@ +using LCU.API.Internal.LoanRateLoadTestingLib.Persistence; +namespace LCU.API.Internal.LoanRateLoadTestingLib.Repositories; + +public abstract class BaseRepository +{ + protected readonly AppDbContext _context; + + public BaseRepository(AppDbContext context) + { + _context = context; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/ILoadTestRunInfoRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/ILoadTestRunInfoRepository.cs new file mode 100644 index 0000000..527e5b2 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/ILoadTestRunInfoRepository.cs @@ -0,0 +1,16 @@ +using LCU.Library.Internal.LCUFilterModels.Pagination; + +namespace LCU.API.Internal.LoanRateLoadTestingLib.Repositories; + +public interface ILoadTestRunInfoRepository +{ + Task?> FindAsync(); + Task FindByIdAsync(int id); + Task?> FindRangeByIdAsync(List ids); + Task> FindFilteredLoadTestRunInfosAsync(LoadTestRunInfoFilter loadTestRunInfoFilter); + Task AddAsync(LoadTestRunInfo loadTestRunInfo); + Task AddRangeAsync(IEnumerable loadTestRunInfos); + void UpdateRange(IEnumerable loadTestRunInfos); + void Remove(LoadTestRunInfo loadTestRunInfo); + void RemoveRange(IEnumerable loadTestRunInfos); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/IRateProcessingInfoRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/IRateProcessingInfoRepository.cs new file mode 100644 index 0000000..db32a5f --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/IRateProcessingInfoRepository.cs @@ -0,0 +1,14 @@ +namespace LCU.API.Internal.LoanRateLoadTestingLib.Repositories; + +public interface IRateProcessingInfoRepository +{ + Task?> FindAsync(); + Task FindByIdAsync(int id); + Task?> FindRangeByIdAsync(List ids); + Task?> FindByLoadTestRunInfoIdAsync(int loadTestRunInfoId); + Task AddAsync(RateProcessingInfo rateProcessingInfo); + Task AddRangeAsync(IEnumerable rateProcessingInfos); + void UpdateRange(IEnumerable rateProcessingInfos); + void Remove(RateProcessingInfo rateProcessingInfo); + void RemoveRange(IEnumerable rateProcessingInfos); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/IUnitOfWork.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/IUnitOfWork.cs new file mode 100644 index 0000000..10f80e3 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/IUnitOfWork.cs @@ -0,0 +1,7 @@ +namespace LCU.API.Internal.LoanRateLoadTestingLib.Repositories; + +public interface IUnitOfWork +{ + Task CompleteAsync(); + void Complete(); +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/LoadTestRunInfoRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/LoadTestRunInfoRepository.cs new file mode 100644 index 0000000..0dd7c3d --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/LoadTestRunInfoRepository.cs @@ -0,0 +1,74 @@ +using LCU.API.Internal.LoanRateLoadTestingLib.Persistence; +using LCU.API.Internal.LoanRateLoadTestingLib.Extensions.Queryables; +using LCU.Library.Internal.LCUFilterModels.Pagination; + +namespace LCU.API.Internal.LoanRateLoadTestingLib.Repositories; + +public class LoadTestRunInfoRepository : BaseRepository, ILoadTestRunInfoRepository +{ + public LoadTestRunInfoRepository(AppDbContext context) : base(context) { } + + public async Task?> FindAsync() + { + return await _context.LoadTestRunInfos.IncludeAll().ToListAsync(); + } + + public async Task FindByIdAsync(int id) + { + return await _context.LoadTestRunInfos.IncludeAll().FirstOrDefaultAsync(loadTestRunInfo => loadTestRunInfo.LoadTestRunInfoId == id); + } + + public async Task?> FindRangeByIdAsync(List ids) + { + return await _context.LoadTestRunInfos + .Where(loadTestRunInfo => ids.Contains(loadTestRunInfo.LoadTestRunInfoId)) + .IncludeAll() + .ToListAsync(); + } + + public async Task> FindFilteredLoadTestRunInfosAsync(LoadTestRunInfoFilter loadTestRunInfoFilter) + { + var results = new PagedResult(); + var skip = (loadTestRunInfoFilter.Page - 1) * loadTestRunInfoFilter.Limit; + + results.Total = await _context.LoadTestRunInfos + .IncludeAll() + .IncludeFilters(loadTestRunInfoFilter) + .CountAsync(); + + results.Items = await _context.LoadTestRunInfos + .IncludeAll() + .IncludeFilters(loadTestRunInfoFilter) + .OrderBySortableFields(loadTestRunInfoFilter) + .Skip(skip) + .Take(loadTestRunInfoFilter.Limit) + .ToArrayAsync(); + + return results; + } + + public async Task AddAsync(LoadTestRunInfo loadTestRunInfo) + { + return (await _context.LoadTestRunInfos.AddAsync(loadTestRunInfo)).Entity; + } + + public async Task AddRangeAsync(IEnumerable loadTestRunInfos) + { + await _context.LoadTestRunInfos.AddRangeAsync(loadTestRunInfos); + } + + public void UpdateRange(IEnumerable loadTestRunInfos) + { + _context.LoadTestRunInfos.UpdateRange(loadTestRunInfos); + } + + public void Remove(LoadTestRunInfo loadTestRunInfo) + { + _context.LoadTestRunInfos.Remove(loadTestRunInfo); + } + + public void RemoveRange(IEnumerable loadTestRunInfos) + { + _context.LoadTestRunInfos.RemoveRange(loadTestRunInfos); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/RateProcessingInfoRepository.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/RateProcessingInfoRepository.cs new file mode 100644 index 0000000..a107db1 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/RateProcessingInfoRepository.cs @@ -0,0 +1,56 @@ +using LCU.API.Internal.LoanRateLoadTestingLib.Persistence; +namespace LCU.API.Internal.LoanRateLoadTestingLib.Repositories; + +public class RateProcessingInfoRepository : BaseRepository, IRateProcessingInfoRepository +{ + public RateProcessingInfoRepository(AppDbContext context) : base(context) { } + + public async Task?> FindAsync() + { + return await _context.RateProcessingInfos.ToListAsync(); + } + + public async Task FindByIdAsync(int id) + { + return await _context.RateProcessingInfos.FirstOrDefaultAsync(rateProcessingInfo => rateProcessingInfo.RateProcessingInfoId == id); + } + + public async Task?> FindRangeByIdAsync(List ids) + { + return await _context.RateProcessingInfos + .Where(rateProcessingInfo => ids.Contains(rateProcessingInfo.RateProcessingInfoId)) + .ToListAsync(); + } + + public async Task?> FindByLoadTestRunInfoIdAsync(int loadTestRunInfoId) + { + return await _context.RateProcessingInfos + .Where(rateProcessingInfo => rateProcessingInfo.LoadTestRunInfoId == loadTestRunInfoId) + .ToListAsync(); + } + + public async Task AddAsync(RateProcessingInfo rateProcessingInfo) + { + return (await _context.RateProcessingInfos.AddAsync(rateProcessingInfo)).Entity; + } + + public async Task AddRangeAsync(IEnumerable rateProcessingInfos) + { + await _context.RateProcessingInfos.AddRangeAsync(rateProcessingInfos); + } + + public void UpdateRange(IEnumerable rateProcessingInfos) + { + _context.RateProcessingInfos.UpdateRange(rateProcessingInfos); + } + + public void Remove(RateProcessingInfo rateProcessingInfo) + { + _context.RateProcessingInfos.Remove(rateProcessingInfo); + } + + public void RemoveRange(IEnumerable rateProcessingInfos) + { + _context.RateProcessingInfos.RemoveRange(rateProcessingInfos); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/UnitOfWork.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/UnitOfWork.cs new file mode 100644 index 0000000..d208bc8 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/Repositories/UnitOfWork.cs @@ -0,0 +1,22 @@ +using LCU.API.Internal.LoanRateLoadTestingLib.Persistence; +namespace LCU.API.Internal.LoanRateLoadTestingLib.Repositories; + +public class UnitOfWork : IUnitOfWork +{ + private readonly AppDbContext _context; + + public UnitOfWork(AppDbContext context) + { + _context = context; + } + + public async Task CompleteAsync() + { + await _context.SaveChangesAsync(); + } + + public void Complete() + { + _context.SaveChanges(); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/using.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/using.cs new file mode 100644 index 0000000..5496b8a --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingLib/using.cs @@ -0,0 +1,2 @@ +global using Microsoft.EntityFrameworkCore; +global using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/LCU.API.Internal.LoanRateLoadTestingSql.refactorlog b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/LCU.API.Internal.LoanRateLoadTestingSql.refactorlog new file mode 100644 index 0000000..3ebc2bc --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/LCU.API.Internal.LoanRateLoadTestingSql.refactorlog @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/LCU.API.Internal.LoanRateLoadTestingSql.sqlproj b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/LCU.API.Internal.LoanRateLoadTestingSql.sqlproj new file mode 100644 index 0000000..22a4d97 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/LCU.API.Internal.LoanRateLoadTestingSql.sqlproj @@ -0,0 +1,67 @@ + + + + Debug + AnyCPU + LCU.API.Internal.LoanRateLoadTestingSql + 2.0 + 4.1 + {0687c40b-33b7-48fd-9611-c3af96987cbe} + Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider + Database + + + LCU.API.Internal.LoanRateLoadTestingSql + LCU.API.Internal.LoanRateLoadTestingSql + 1033, CI + BySchemaAndSchemaType + True + v4.7.2 + CS + Properties + False + True + True + + + bin\Release\ + $(MSBuildProjectName).sql + False + pdbonly + true + false + true + prompt + 4 + + + bin\Debug\ + $(MSBuildProjectName).sql + false + true + full + false + true + true + prompt + 4 + + + 11.0 + + True + 11.0 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/LoadTestRunInfo.sql b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/LoadTestRunInfo.sql new file mode 100644 index 0000000..b1fe827 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/LoadTestRunInfo.sql @@ -0,0 +1,9 @@ +CREATE TABLE [dbo].[LoadTestRunInfo] +( + [LoadTestRunInfoId] INT NOT NULL PRIMARY KEY IDENTITY, + [StartTimeStamp] DATETIME NOT NULL, + [EndTimeStamp] DATETIME NULL, + [RateType] INT NOT NULL, + [RateProcessingInfosCount] INT NOT NULL, + [RateProcessingInfosCompleteCount] INT NOT NULL +) diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/RateProcessingInfo.sql b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/RateProcessingInfo.sql new file mode 100644 index 0000000..481b35b --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateLoadTestingSql/RateProcessingInfo.sql @@ -0,0 +1,13 @@ +CREATE TABLE [dbo].[RateProcessingInfo] +( + [RateProcessingInfoId] INT NOT NULL PRIMARY KEY IDENTITY, + [LoadTestRunInfoId] INT NOT NULL, + [Payload] VARCHAR(MAX) NOT NULL, + [Rate] DECIMAL(18, 3) NOT NULL, + [Column] VARCHAR(50) NULL, + [Row] VARCHAR(50) NULL, + [RateProcessingStatusType] INT NOT NULL, + [RateProcessingResultType] INT NOT NULL DEFAULT 0, + [ProcessingMessage] VARCHAR(1000) NULL, + CONSTRAINT [FK_RateProcessingInfo_LoadTestRunInfo] FOREIGN KEY ([LoadTestRunInfoId]) REFERENCES [LoadTestRunInfo]([LoadTestRunInfoId]) +) diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/LCU.API.Internal.LoanRateSql.refactorlog b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/LCU.API.Internal.LoanRateSql.refactorlog new file mode 100644 index 0000000..b502665 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/LCU.API.Internal.LoanRateSql.refactorlog @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/LCU.API.Internal.LoanRateSql.sqlproj b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/LCU.API.Internal.LoanRateSql.sqlproj new file mode 100644 index 0000000..9861861 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/LCU.API.Internal.LoanRateSql.sqlproj @@ -0,0 +1,76 @@ + + + + Debug + AnyCPU + LCU.API.Internal.LoanRateSql + 2.0 + 4.1 + {33cbc5ad-1b92-4d5f-a79f-4464c79c5f0c} + Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider + Database + + + LCU.API.Internal.LoanRateSql + LCU.API.Internal.LoanRateSql + 1033, CI + BySchemaAndSchemaType + True + v4.7.2 + CS + Properties + False + True + True + + + bin\Release\ + $(MSBuildProjectName).sql + False + pdbonly + true + false + true + prompt + 4 + + + bin\Debug\ + $(MSBuildProjectName).sql + false + true + full + false + true + true + prompt + 4 + + + 11.0 + + True + 11.0 + + + + + + + + + + + + + + + + + + + ..\LCU.API.Internal.LoanRate\bin\Debug\LCU.Library.Internal.LcuEvaluationSql.dacpac + False + + + \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/Rate.sql b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/Rate.sql new file mode 100644 index 0000000..560f0bd --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/Rate.sql @@ -0,0 +1,15 @@ +CREATE TABLE [dbo].[Rate] +( + [RateId] INT NOT NULL PRIMARY KEY IDENTITY, + [RateUploadInfoId] INT NULL, + [ClosingRateUploadInfoId] INT NULL, + [Column] VARCHAR(50) NULL, + [Row] VARCHAR(50) NULL, + [RateSheetId] INT NOT NULL, + [Value] DECIMAL(18, 3) NOT NULL, + [RateStartDate] DATETIME2 NOT NULL, + [RateEndDate] DATETIME2 NULL, + CONSTRAINT [FK_Rate_RateUploadInfo] FOREIGN KEY ([RateUploadInfoId]) REFERENCES [RateUploadInfo]([RateUploadInfoId]), + CONSTRAINT [FK_Rate_ClosingRateUploadInfo] FOREIGN KEY ([ClosingRateUploadInfoId]) REFERENCES [RateUploadInfo]([RateUploadInfoId]), + CONSTRAINT [FK_Rate_RateSheet] FOREIGN KEY ([RateSheetId]) REFERENCES [RateSheet]([RateSheetId]) +) diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateEvaluation.sql b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateEvaluation.sql new file mode 100644 index 0000000..f1e9a63 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateEvaluation.sql @@ -0,0 +1,8 @@ +CREATE TABLE [dbo].[RateEvaluation] +( + [RateEvaluationId] INT NOT NULL PRIMARY KEY IDENTITY, + [RateId] INT NOT NULL, + [EvaluationDetailId] INT NOT NULL, + CONSTRAINT [FK_RateEvaluation_Rate] FOREIGN KEY ([RateId]) REFERENCES [Rate]([RateId]), + CONSTRAINT [FK_RateEvaluation_EvaluationDetail] FOREIGN KEY ([EvaluationDetailId]) REFERENCES [EvaluationDetail]([EvaluationDetailId]) +) diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateSheet.sql b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateSheet.sql new file mode 100644 index 0000000..d37ee18 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateSheet.sql @@ -0,0 +1,7 @@ +CREATE TABLE [dbo].[RateSheet] +( + [RateSheetId] INT NOT NULL PRIMARY KEY IDENTITY, + [RateType] INT NOT NULL, + [TimeCreated] DATETIME2 NOT NULL, + [CreatedBy] VARCHAR(500) NOT NULL +) diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateSheetCell.sql b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateSheetCell.sql new file mode 100644 index 0000000..bab068e --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateSheetCell.sql @@ -0,0 +1,12 @@ +CREATE TABLE [dbo].[RateSheetCell] +( + [RateSheetCellId] INT NOT NULL PRIMARY KEY IDENTITY, + [RateSheetId] INT NOT NULL, + [Column] VARCHAR(50) NOT NULL, + [ColumnSpan] INT NULL, + [Row] VARCHAR(50) NOT NULL, + [RowSpan] INT NULL, + [Value] VARCHAR(500) NOT NULL, + [RateSheetCellType] INT NOT NULL + CONSTRAINT [FK_RateSheet_RateSheetCell] FOREIGN KEY ([RateSheetId]) REFERENCES [RateSheet]([RateSheetId]) +) diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateUploadInfo.sql b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateUploadInfo.sql new file mode 100644 index 0000000..929448e --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateSql/RateUploadInfo.sql @@ -0,0 +1,10 @@ +CREATE TABLE [dbo].[RateUploadInfo] +( + [RateUploadInfoId] INT NOT NULL PRIMARY KEY IDENTITY, + [Version] INT NOT NULL, + [Filename] VARCHAR(1000) NOT NULL, + [EffectiveDate] DATETIME2 NOT NULL, + [TimeCreated] DATETIME2 NOT NULL, + [CreatedBy] VARCHAR(500) NOT NULL, + [RateCount] INT NOT NULL DEFAULT 0 +) diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/RateControllerTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/RateControllerTest.cs new file mode 100644 index 0000000..131117d --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/RateControllerTest.cs @@ -0,0 +1,716 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.API.Internal.LoanRate.Controllers; +using LCU.API.Internal.LoanRate.Services; +namespace LCU.API.Internal.LoanRateTests.ControllerTests; + +public class RateControllerTest +{ + private readonly Mock _mockRateService; + private readonly RateController _controller; + + public RateControllerTest() + { + _mockRateService = new Mock(); + _controller = new RateController(_mockRateService.Object); + } + + [Fact] + public async void Rate_Get_OK() + { + var rateId = 1; + var rates = new List + { + new() + { + RateId = rateId + } + }; + + _mockRateService.Setup(x => x.GetAsync()).ReturnsAsync(rates).Verifiable(); + + var result = await _controller.Get(); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + + var RateResult = status.Value as List; + Assert.NotNull(RateResult); + Assert.Single(RateResult); + Assert.Equal(rateId, RateResult.First().RateId); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_Get_NoContent() + { + var rates = new List(); + + _mockRateService.Setup(x => x.GetAsync()).ReturnsAsync(rates).Verifiable(); + + var result = await _controller.Get(); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as NoContentResult; + Assert.NotNull(status); + Assert.Equal(204, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_Get_InternalServerError() + { + _mockRateService.Setup(x => x.GetAsync()) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.Get(); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_GetById_OK() + { + var rateId = 1; + var rate = new Rate + { + RateId = rateId + }; + + _mockRateService.Setup(x => x.GetByIdAsync(rateId)) + .ReturnsAsync(rate).Verifiable(); + + var result = await _controller.GetById(rateId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + + var RateResult = status.Value as Rate; + Assert.NotNull(RateResult); + Assert.Equal(rateId, RateResult.RateId); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_GetById_NoContent() + { + var rateId = 1; + Rate? rate = null; + + _mockRateService.Setup(x => x.GetByIdAsync(rateId)) + .ReturnsAsync(rate).Verifiable(); + + var result = await _controller.GetById(rateId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as NoContentResult; + Assert.NotNull(status); + Assert.Equal(204, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_GetById_BadRequest() + { + var rateId = 0; + + var result = await _controller.GetById(rateId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void Rate_GetById_InternalServerError() + { + var rateId = 1; + _mockRateService.Setup(x => x.GetByIdAsync(rateId)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.GetById(rateId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_GetListById_OK() + { + var rateIds = new List + { + 1, + 2, + 3 + }; + var rates = new List + { + new() { RateId = rateIds[0] }, + new() { RateId = rateIds[1] }, + new() { RateId = rateIds[2] } + }; + + _mockRateService.Setup(x => x.GetRangeByIdAsync(rateIds)) + .ReturnsAsync(rates).Verifiable(); + + var result = await _controller.GetListById(rateIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + + var rateResult = status.Value as List; + Assert.NotNull(rateResult); + Assert.Equal(rateIds[0], rateResult[0].RateId); + Assert.Equal(rateIds[1], rateResult[1].RateId); + Assert.Equal(rateIds[2], rateResult[2].RateId); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_GetListById_NoContent() + { + var rateIds = new List + { + 1, + 2, + 3 + }; + List? rates = null; + + _mockRateService.Setup(x => x.GetRangeByIdAsync(rateIds)) + .ReturnsAsync(rates).Verifiable(); + + var result = await _controller.GetListById(rateIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as NoContentResult; + Assert.NotNull(status); + Assert.Equal(204, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_GetListById_BadRequest() + { + List? rateIds = null; + + var result = await _controller.GetListById(rateIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void Rate_GetListById_InternalServerError() + { + var rateIds = new List + { + 1, + 2, + 3 + }; + _mockRateService.Setup(x => x.GetRangeByIdAsync(rateIds)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.GetListById(rateIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_Post_CreatedAtAction() + { + var rateId = 1; + var Value = Guid.NewGuid().ToString(); + var rate = new Rate + { + Value = 5.59M + }; + var rateReturned = new Rate + { + RateId = rateId, + Value = 5.59M + }; + + _mockRateService.Setup(x => x.SaveAsync(rate)) + .ReturnsAsync(rateReturned).Verifiable(); + + var result = await _controller.Post(rate); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as CreatedAtActionResult; + Assert.NotNull(status); + Assert.Equal(201, status.StatusCode); + + var resultValue = status.Value as Rate; + Assert.NotNull(result); + Assert.NotNull(resultValue); + Assert.Equal(rateReturned.RateId, resultValue.RateId); + Assert.Equal(rateReturned.Value, resultValue.Value); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_Post_BadRequest() + { + Rate? rate = null; + + var result = await _controller.Post(rate); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void Rate_Post_InternalServerError() + { + var rate = new Rate + { + RateId = 1 + }; + _mockRateService.Setup(x => x.SaveAsync(rate)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.Post(rate); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_SaveRange_Ok() + { + var rates = new List + { + new() { Value = 5.60M }, + new() { Value = 5.59M }, + new() { Value = 5.61M } + }; + + _mockRateService.Setup(x => x.SaveRangeAsync(rates)); + + var result = await _controller.SaveRange(rates); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_SaveRange_BadRequest() + { + List? rates = null; + + var result = await _controller.SaveRange(rates); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void Rate_SaveRange_InternalServerError() + { + var rates = new List + { + new() { Value = 5.60M }, + new() { Value = 5.59M }, + new() { Value = 5.61M } + }; + _mockRateService.Setup(x => x.SaveRangeAsync(rates)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.SaveRange(rates); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_Put_OK() + { + var rateId = 1; + var value = 5.60M; + var rate = new Rate + { + RateId = rateId, + Value = value + }; + var rateReturned = new Rate + { + RateId = rateId, + Value = value + }; + + _mockRateService.Setup(x => x.UpdateAsync(rate)) + .ReturnsAsync(rateReturned).Verifiable(); + + var result = await _controller.Put(rate); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + + var rateResult = status.Value as Rate; + Assert.NotNull(rateResult); + Assert.Equal(rate.Value, rateResult.Value); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_Put_Status406NotAcceptable() + { + var rate = new Rate + { + RateId = 1 + }; + + var result = await _controller.Put(rate); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(406, status.StatusCode); + } + + [Fact] + public async void Rate_Put_BadRequest_NoIds() + { + var rate = new Rate + { + Value = 5.59M + }; + + var result = await _controller.Put(rate); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + _mockRateService.Verify(x => x.GetAsync(), Times.Never); + } + + [Fact] + public async void Rate_Put_BadRequest() + { + Rate? rate = null; + + var result = await _controller.Put(rate); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void Rate_Put_InternalServerError() + { + var rate = new Rate + { + RateId = 1 + }; + _mockRateService.Setup(x => x.UpdateAsync(rate)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.Put(rate); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_UpdateRange_OK() + { + var values = new List + { + 5.60M, + 5.61M, + 5.62M + }; + var rates = new List + { + new() + { + RateId = 1, + Value = values[0] + }, + new() + { + RateId = 2, + Value = values[1] + }, + new() + { + RateId = 3, + Value = values[2] + } + }; + + var ratesReturned = new List + { + new() + { + RateId = 1, + Value = values[0] + }, + new() + { + RateId = 2, + Value = values[1] + }, + new() + { + RateId = 3, + Value = values[2] + } + }; + + _mockRateService.Setup(x => x.UpdateRangeAsync(rates)) + .ReturnsAsync(ratesReturned).Verifiable(); + + var result = await _controller.UpdateRange(rates); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + + var rateResult = status.Value as List; + Assert.NotNull(rateResult); + Assert.Equal(rates[0].Value, rateResult[0].Value); + Assert.Equal(rates[1].Value, rateResult[1].Value); + Assert.Equal(rates[2].Value, rateResult[2].Value); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_UpdateRange_BadRequest_NoIds() + { + var rates = new List + { + new () + { + Value = 5.60M + }, + new () + { + Value = 5.61M + } + }; + + var result = await _controller.UpdateRange(rates); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + _mockRateService.Verify(x => x.GetAsync(), Times.Never); + } + + [Fact] + public async void Rate_UpdateRange_BadRequest() + { + List? rates = null; + + var result = await _controller.UpdateRange(rates); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void Rate_UpdateRange_InternalServcerError() + { + var rates = new List + { + new () + { + RateId = 1, + Value = 5.60M + }, + new () + { + RateId = 2, + Value = 5.61M + } + }; + _mockRateService.Setup(x => x.UpdateRangeAsync(rates)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.UpdateRange(rates); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_Delete_OK() + { + var rateId = 1; + + _mockRateService.Setup(x => x.DeleteAsync(rateId)).Verifiable(); + + var result = await _controller.Delete(rateId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_Delete_BadRequest() + { + var rateId = 0; + + var result = await _controller.Delete(rateId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void Rate_Delete_InternalServerError() + { + var rateId = 1; + _mockRateService.Setup(x => x.DeleteAsync(rateId)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.Delete(rateId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_DeleteRange_OK() + { + var rateIds = new List + { + 1, + 1, + 1 + }; + + _mockRateService.Setup(x => x.DeleteRangeAsync(rateIds)).Verifiable(); + + var result = await _controller.DeleteRange(rateIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + _mockRateService.Verify(); + } + + [Fact] + public async void Rate_DeleteRange_BadRequest() + { + var rateIds = new List(); + + var result = await _controller.DeleteRange(rateIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void Rate_DeleteRange_InternalServerError() + { + var rateIds = new List + { + 1 + }; + _mockRateService.Setup(x => x.DeleteRangeAsync(rateIds)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.DeleteRange(rateIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateService.Verify(); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/RateEvaluationControllerTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/RateEvaluationControllerTest.cs new file mode 100644 index 0000000..f7d793d --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/RateEvaluationControllerTest.cs @@ -0,0 +1,765 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.API.Internal.LoanRate.Services; +using LCU.API.Internal.LoanRate.Controllers; +namespace LCU.API.Internal.LoanRateTests.ControllerTests; + +public class RateEvaluationControllerTest +{ + private readonly Mock _mockRateEvaluationService; + private readonly RateEvaluationController _controller; + + public RateEvaluationControllerTest() + { + _mockRateEvaluationService = new Mock(); + _controller = new RateEvaluationController(_mockRateEvaluationService.Object); + } + + [Fact] + public async void RateEvaluation_Get_OK() + { + var rateEvaluationId = 1; + var rateEvaluations = new List + { + new() + { + RateEvaluationId = rateEvaluationId + } + }; + + _mockRateEvaluationService.Setup(x => x.GetAsync()).ReturnsAsync(rateEvaluations).Verifiable(); + + var result = await _controller.Get(); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + + var RateEvaluationResult = status.Value as List; + Assert.NotNull(RateEvaluationResult); + Assert.Single(RateEvaluationResult); + Assert.Equal(rateEvaluationId, RateEvaluationResult.First().RateEvaluationId); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_Get_NoContent() + { + var rateEvaluations = new List(); + + _mockRateEvaluationService.Setup(x => x.GetAsync()).ReturnsAsync(rateEvaluations).Verifiable(); + + var result = await _controller.Get(); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as NoContentResult; + Assert.NotNull(status); + Assert.Equal(204, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_Get_InternalServerError() + { + _mockRateEvaluationService.Setup(x => x.GetAsync()) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.Get(); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_GetById_OK() + { + var rateEvaluationId = 1; + var rateEvaluation = new RateEvaluation + { + RateEvaluationId = rateEvaluationId + }; + + _mockRateEvaluationService.Setup(x => x.GetByIdAsync(rateEvaluationId)) + .ReturnsAsync(rateEvaluation).Verifiable(); + + var result = await _controller.GetById(rateEvaluationId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + + var rateEvaluationResult = status.Value as RateEvaluation; + Assert.NotNull(rateEvaluationResult); + Assert.Equal(rateEvaluationId, rateEvaluationResult.RateEvaluationId); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_GetById_NoContent() + { + var rateEvaluationId = 1; + RateEvaluation? rateEvaluation = null; + + _mockRateEvaluationService.Setup(x => x.GetByIdAsync(rateEvaluationId)) + .ReturnsAsync(rateEvaluation).Verifiable(); + + var result = await _controller.GetById(rateEvaluationId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as NoContentResult; + Assert.NotNull(status); + Assert.Equal(204, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_GetById_BadRequest() + { + var rateEvaluationId = 0; + + var result = await _controller.GetById(rateEvaluationId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void RateEvaluation_GetById_InternalServerError() + { + var rateEvaluationId = 1; + _mockRateEvaluationService.Setup(x => x.GetByIdAsync(rateEvaluationId)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.GetById(rateEvaluationId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_GetListById_OK() + { + var rateEvaluationIds = new List + { + 1, + 2, + 3 + }; + var rateEvaluations = new List + { + new() { RateEvaluationId = rateEvaluationIds[0] }, + new() { RateEvaluationId = rateEvaluationIds[1] }, + new() { RateEvaluationId = rateEvaluationIds[2] } + }; + + _mockRateEvaluationService.Setup(x => x.GetRangeByIdAsync(rateEvaluationIds)) + .ReturnsAsync(rateEvaluations).Verifiable(); + + var result = await _controller.GetListById(rateEvaluationIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + + var rateEvaluationResult = status.Value as List; + Assert.NotNull(rateEvaluationResult); + Assert.Equal(rateEvaluationIds[0], rateEvaluationResult[0].RateEvaluationId); + Assert.Equal(rateEvaluationIds[1], rateEvaluationResult[1].RateEvaluationId); + Assert.Equal(rateEvaluationIds[2], rateEvaluationResult[2].RateEvaluationId); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_GetListById_NoContent() + { + var rateEvaluationIds = new List + { + 1, + 2, + 3 + }; + List? rateEvaluations = null; + + _mockRateEvaluationService.Setup(x => x.GetRangeByIdAsync(rateEvaluationIds)) + .ReturnsAsync(rateEvaluations).Verifiable(); + + var result = await _controller.GetListById(rateEvaluationIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as NoContentResult; + Assert.NotNull(status); + Assert.Equal(204, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_GetListById_BadRequest() + { + List? rateEvaluationIds = null; + + var result = await _controller.GetListById(rateEvaluationIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void RateEvaluation_GetListById_InternalServerError() + { + var rateEvaluationIds = new List + { + 1, + 2, + 3 + }; + _mockRateEvaluationService.Setup(x => x.GetRangeByIdAsync(rateEvaluationIds)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.GetListById(rateEvaluationIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_Post_CreatedAtAction() + { + var rateEvaluationId = 1; + var Value = Guid.NewGuid().ToString(); + var rateEvaluation = new RateEvaluation + { + EvaluationDetailId = 1 + }; + var rateEvaluationReturned = new RateEvaluation + { + RateEvaluationId = rateEvaluationId, + EvaluationDetailId = 1 + }; + + _mockRateEvaluationService.Setup(x => x.SaveAsync(rateEvaluation)) + .ReturnsAsync(rateEvaluationReturned).Verifiable(); + + var result = await _controller.Post(rateEvaluation); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as CreatedAtActionResult; + Assert.NotNull(status); + Assert.Equal(201, status.StatusCode); + + var resultValue = status.Value as RateEvaluation; + Assert.NotNull(result); + Assert.NotNull(resultValue); + Assert.Equal(rateEvaluationReturned.RateEvaluationId, resultValue.RateEvaluationId); + Assert.Equal(rateEvaluationReturned.EvaluationDetailId, resultValue.EvaluationDetailId); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_Post_BadRequest() + { + RateEvaluation? rateEvaluation = null; + + var result = await _controller.Post(rateEvaluation); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void RateEvaluation_Post_InternalServerError() + { + var rateEvaluation = new RateEvaluation + { + EvaluationDetailId = 1 + }; + _mockRateEvaluationService.Setup(x => x.SaveAsync(rateEvaluation)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.Post(rateEvaluation); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_SaveRange_Ok() + { + var rateEvaluations = new List + { + new() { EvaluationDetailId = 1 }, + new() { EvaluationDetailId = 2 }, + new() { EvaluationDetailId = 3 } + }; + + _mockRateEvaluationService.Setup(x => x.SaveRangeAsync(rateEvaluations)); + + var result = await _controller.SaveRange(rateEvaluations); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_SaveRange_BadRequest() + { + List? rateEvaluations = null; + + var result = await _controller.SaveRange(rateEvaluations); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void RateEvaluation_SaveRange_InternalServerError() + { + var rateEvaluations = new List + { + new() { EvaluationDetailId = 1 }, + new() { EvaluationDetailId = 2 }, + new() { EvaluationDetailId = 3 } + }; + _mockRateEvaluationService.Setup(x => x.SaveRangeAsync(rateEvaluations)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.SaveRange(rateEvaluations); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_Put_OK() + { + var rateEvaluationId = 1; + var rateEvaluation = new RateEvaluation + { + RateEvaluationId = rateEvaluationId, + EvaluationDetailId = 1 + }; + var RateEvaluationReturned = new RateEvaluation + { + RateEvaluationId = rateEvaluationId, + EvaluationDetailId = 1 + }; + + _mockRateEvaluationService.Setup(x => x.UpdateAsync(rateEvaluation)) + .ReturnsAsync(RateEvaluationReturned).Verifiable(); + + var result = await _controller.Put(rateEvaluation); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + + var rateEvaluationResult = status.Value as RateEvaluation; + Assert.NotNull(rateEvaluationResult); + Assert.Equal(rateEvaluation.EvaluationDetailId, rateEvaluationResult.EvaluationDetailId); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_Put_Status406NotAcceptable() + { + var rateEvaluation = new RateEvaluation + { + RateEvaluationId = 1 + }; + + var result = await _controller.Put(rateEvaluation); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(406, status.StatusCode); + } + + [Fact] + public async void RateEvaluation_Put_BadRequest_NoIds() + { + var rateEvaluation = new RateEvaluation + { + EvaluationDetailId = 1 + }; + + var result = await _controller.Put(rateEvaluation); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + _mockRateEvaluationService.Verify(x => x.GetAsync(), Times.Never); + } + + [Fact] + public async void RateEvaluation_Put_BadRequest() + { + RateEvaluation? rateEvaluation = null; + + var result = await _controller.Put(rateEvaluation); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void RateEvaluation_Put_InternalServerError() + { + var rateEvaluation = new RateEvaluation + { + RateEvaluationId = 1 + }; + _mockRateEvaluationService.Setup(x => x.UpdateAsync(rateEvaluation)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.Put(rateEvaluation); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_UpdateRange_OK() + { + var evaluationDetailIds = new List + { + 1, + 2, + 3 + }; + var rateEvaluations = new List + { + new() + { + RateEvaluationId = 1, + EvaluationDetailId = evaluationDetailIds[0] + }, + new() + { + RateEvaluationId = 2, + EvaluationDetailId = evaluationDetailIds[1] + }, + new() + { + RateEvaluationId = 3, + EvaluationDetailId = evaluationDetailIds[2] + } + }; + + var rateEvaluationsReturned = new List + { + new() + { + RateEvaluationId = 1, + EvaluationDetailId = evaluationDetailIds[0] + }, + new() + { + RateEvaluationId = 2, + EvaluationDetailId = evaluationDetailIds[1] + }, + new() + { + RateEvaluationId = 3, + EvaluationDetailId = evaluationDetailIds[2] + } + }; + + _mockRateEvaluationService.Setup(x => x.UpdateRangeAsync(rateEvaluations)) + .ReturnsAsync(rateEvaluationsReturned).Verifiable(); + + var result = await _controller.UpdateRange(rateEvaluations); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + + var rateEvaluationResult = status.Value as List; + Assert.NotNull(rateEvaluationResult); + Assert.Equal(rateEvaluations[0].EvaluationDetailId, rateEvaluationResult[0].EvaluationDetailId); + Assert.Equal(rateEvaluations[1].EvaluationDetailId, rateEvaluationResult[1].EvaluationDetailId); + Assert.Equal(rateEvaluations[2].EvaluationDetailId, rateEvaluationResult[2].EvaluationDetailId); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_UpdateRange_BadRequest_NoIds() + { + var rateEvaluations = new List + { + new () + { + EvaluationDetailId = 1 + }, + new () + { + EvaluationDetailId = 2 + } + }; + + var result = await _controller.UpdateRange(rateEvaluations); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + _mockRateEvaluationService.Verify(x => x.GetAsync(), Times.Never); + } + + [Fact] + public async void RateEvaluation_UpdateRange_BadRequest() + { + List? rateEvaluations = null; + + var result = await _controller.UpdateRange(rateEvaluations); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void RateEvaluation_UpdateRange_InternalServcerError() + { + var rateEvaluations = new List + { + new () + { + RateEvaluationId = 1, + EvaluationDetailId = 1 + }, + new () + { + RateEvaluationId = 2, + EvaluationDetailId = 2 + } + }; + _mockRateEvaluationService.Setup(x => x.UpdateRangeAsync(rateEvaluations)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.UpdateRange(rateEvaluations); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_Delete_OK() + { + var rateEvaluationId = 1; + + _mockRateEvaluationService.Setup(x => x.DeleteAsync(rateEvaluationId)).Verifiable(); + + var result = await _controller.Delete(rateEvaluationId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_Delete_BadRequest() + { + var rateEvaluationId = 0; + + var result = await _controller.Delete(rateEvaluationId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void RateEvaluation_Delete_InternalServerError() + { + var rateEvaluationId = 1; + _mockRateEvaluationService.Setup(x => x.DeleteAsync(rateEvaluationId)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.Delete(rateEvaluationId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_DeleteRange_OK() + { + var rateEvaluationIds = new List + { + 1, + 1, + 1 + }; + + _mockRateEvaluationService.Setup(x => x.DeleteRangeAsync(rateEvaluationIds)).Verifiable(); + + var result = await _controller.DeleteRange(rateEvaluationIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_DeleteRange_BadRequest() + { + var rateEvaluationIds = new List(); + + var result = await _controller.DeleteRange(rateEvaluationIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void RateEvaluation_DeleteRange_InternalServerError() + { + var rateEvaluationIds = new List + { + 1 + }; + _mockRateEvaluationService.Setup(x => x.DeleteRangeAsync(rateEvaluationIds)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.DeleteRange(rateEvaluationIds); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_CountByEvaluationDetailId_OK() + { + var evaluationDetailId = DateTime.Now.Millisecond; + var count = DateTime.Now.Minute; + + _mockRateEvaluationService.Setup(x => x.CountByEvaluationDetailIdAsync(evaluationDetailId)).ReturnsAsync(count).Verifiable(); + + var result = await _controller.CountByEvaluationDetailId(evaluationDetailId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + Assert.Equal(count, status.Value); + _mockRateEvaluationService.Verify(); + } + + [Fact] + public async void RateEvaluation_CountByEvaluationDetailId_BadRequest() + { + var evaluationDetailId = 0; + + var result = await _controller.CountByEvaluationDetailId(evaluationDetailId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void RateEvaluation_CountByEvaluationDetailId_InternalServerError() + { + var evaluationDetailId = DateTime.Now.Millisecond; + _mockRateEvaluationService.Setup(x => x.CountByEvaluationDetailIdAsync(evaluationDetailId)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.CountByEvaluationDetailId(evaluationDetailId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateEvaluationService.Verify(); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/RateUploadInfoControllerTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/RateUploadInfoControllerTest.cs new file mode 100644 index 0000000..00f7fb4 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/RateUploadInfoControllerTest.cs @@ -0,0 +1,232 @@ +using System.Linq.Dynamic.Core; +using LCU.API.Internal.LoanRate.Controllers; +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.LcuClassPatterns.CrudTests.Tests; +using LCU.Library.Internal.LcuClassPatterns.CrudTests.Utilities; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LoanRateModels.Responses; +using Microsoft.AspNetCore.Mvc; + +namespace LCU.API.Internal.LoanRateTests.ControllerTests; + +public class RateUploadInfoControllerTest : CrudControllerTest +{ + private readonly Mock _mockRateUploadInfoService = new(); + private readonly RateUploadInfoController _controller; + + public RateUploadInfoControllerTest() + { + _controller = new RateUploadInfoController(_mockRateUploadInfoService.Object); + } + + [Fact] + public async void RateUploadInfo_GetLatest_OK() + { + var rateUploadInfo = new RateUploadInfo + { + RateUploadInfoId = (int)typeof(int).GetRandomValue() + }; + + _mockRateUploadInfoService.Setup(x => x.GetLatestAsync()).ReturnsAsync(rateUploadInfo).Verifiable(); + + var result = await _controller.GetLatest(); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + _mockRateUploadInfoService.Verify(); + } + + [Fact] + public async void RateUploadInfo_GetLatest_NoContent() + { + RateUploadInfo? rateUploadInfo = null; + + _mockRateUploadInfoService.Setup(x => x.GetLatestAsync()).ReturnsAsync(rateUploadInfo).Verifiable(); + + var result = await _controller.GetLatest(); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as NoContentResult; + Assert.NotNull(status); + Assert.Equal(204, status.StatusCode); + _mockRateUploadInfoService.Verify(); + } + + [Fact] + public async void RateUploadInfo_GetLatest_InternalServerError() + { + _mockRateUploadInfoService.Setup(x => x.GetLatestAsync()) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.GetLatest(); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateUploadInfoService.Verify(); + } + + [Fact] + public async void RateUploadInfo_GetFilteredRateUploadInfo_OK() + { + var request = new RateUploadInfoFilter + { + EffectiveDate = (DateTime)typeof(DateTime).GetRandomValue() + }; + + var response = new RateUploadInfoFilterResult + { + Items = new List + { + new() { RateUploadInfoId = (int)typeof(int).GetRandomValue() }, + new() { RateUploadInfoId = (int)typeof(int).GetRandomValue() }, + new() { RateUploadInfoId = (int)typeof(int).GetRandomValue() } + } + }; + + _mockRateUploadInfoService.Setup(x => x.GetFilteredRateUploadInfo(request)).ReturnsAsync(response).Verifiable(); + + var result = await _controller.GetFilteredRateUploadInfo(request); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + _mockRateUploadInfoService.Verify(); + } + + [Fact] + public async void RateUploadInfo_GetFilteredRateUploadInfo_InternalServerError() + { + var request = new RateUploadInfoFilter + { + EffectiveDate = (DateTime)typeof(DateTime).GetRandomValue() + }; + + _mockRateUploadInfoService.Setup(x => x.GetFilteredRateUploadInfo(request)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.GetFilteredRateUploadInfo(request); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateUploadInfoService.Verify(); + } + + [Fact] + public async void RateUploadInfo_ComplexDelete_OK() + { + var rateUploadInfoId = (int)typeof(int).GetRandomValue(); + + _mockRateUploadInfoService.Setup(x => x.ComplexDelete(rateUploadInfoId)).Verifiable(); + + var result = await _controller.ComplexDelete(rateUploadInfoId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + _mockRateUploadInfoService.Verify(); + } + + [Fact] + public async void RateUploadInfo_ComplexDelete_BadRequest() + { + var rateUploadInfoId = 0; + + var result = await _controller.ComplexDelete(rateUploadInfoId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void RateUploadInfo_ComplexDelete_InternalServerError() + { + var rateUploadInfoId = (int)typeof(int).GetRandomValue(); + + _mockRateUploadInfoService.Setup(x => x.ComplexDelete(rateUploadInfoId)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.ComplexDelete(rateUploadInfoId); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateUploadInfoService.Verify(); + } + + [Fact] + public async void RateUploadInfo_ComplexSave_OK() + { + var rateUploadInfoRequest = new RateUploadInfoRequest + { + EffectiveDate = (DateTime)typeof(DateTime).GetRandomValue(), + FileName = (string)typeof(string).GetRandomValue() + }; + + _mockRateUploadInfoService.Setup(x => x.ComplexSave(rateUploadInfoRequest)).Verifiable(); + + var result = await _controller.ComplexSave(rateUploadInfoRequest); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as OkObjectResult; + Assert.NotNull(status); + Assert.Equal(200, status.StatusCode); + _mockRateUploadInfoService.Verify(); + } + + [Fact] + public async void RateUploadInfo_ComplexSave_BadRequest() + { + RateUploadInfoRequest? rateUploadInfoRequest = null; + + var result = await _controller.ComplexSave(rateUploadInfoRequest); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as BadRequestObjectResult; + Assert.NotNull(status); + Assert.Equal(400, status.StatusCode); + } + + [Fact] + public async void RateUploadInfo_ComplexSave_InternalServerError() + { + var rateUploadInfoRequest = new RateUploadInfoRequest + { + EffectiveDate = (DateTime)typeof(DateTime).GetRandomValue(), + FileName = (string)typeof(string).GetRandomValue() + }; + + _mockRateUploadInfoService.Setup(x => x.ComplexSave(rateUploadInfoRequest)) + .Throws(new Exception("Test")).Verifiable(); + + var result = await _controller.ComplexSave(rateUploadInfoRequest); + Assert.NotNull(result); + + Assert.IsType(result); + var status = result as StatusCodeResult; + Assert.NotNull(status); + Assert.Equal(500, status.StatusCode); + _mockRateUploadInfoService.Verify(); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/StatusControllerTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/StatusControllerTest.cs new file mode 100644 index 0000000..904bd31 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ControllerTests/StatusControllerTest.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Mvc; +using LCU.Library.Internal.StatusModels.Models; +using LCU.API.Internal.LoanRate.Controllers; +using LCU.API.Internal.LoanRate.Services; +namespace LCU.API.Internal.LoanRateTests.ControllerTests; + +public class StatusControllerTest +{ + private readonly StatusController controller; + private readonly Mock _mockStatusService; + + public StatusControllerTest() + { + _mockStatusService = new Mock(); + controller = new StatusController(_mockStatusService.Object); + } + + [Fact] + public void CreateStatus_Ok() + { + // Arrange + var status = new EnvironmentView() + { + Application = "Test.Application.Name", + CurrentDate = DateTime.Now, + BuildVersion = "1.1.1", + BuildDate = DateTime.Now.AddDays(-1), + Environment = "TestEnvironment" + }; + + _mockStatusService.Setup(service => service.GetEnviromentView()).Returns(status); + + // Act + var result = controller.Get(); + + // Assert + Assert.NotNull(result); + var okResult = result as OkObjectResult; + Assert.NotNull(okResult); + Assert.Equal(200, okResult.StatusCode); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/LCU.API.Internal.LoanRateTests.csproj b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/LCU.API.Internal.LoanRateTests.csproj new file mode 100644 index 0000000..df44261 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/LCU.API.Internal.LoanRateTests.csproj @@ -0,0 +1,32 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/ComplexEvaluationDetailServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/ComplexEvaluationDetailServiceTest.cs new file mode 100644 index 0000000..6ec0236 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/ComplexEvaluationDetailServiceTest.cs @@ -0,0 +1,191 @@ +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LcuEvaluation.Services; +using LCU.Library.Internal.LcuEvaluationModels.Entities; +using LCU.Library.Internal.LcuEvaluationModels.Requests; +using LCU.Library.Internal.LoanRateModels.Entities; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests; + +public class ComplexEvaluationDetailServiceTest +{ + private readonly Mock _mockRateEvaluationService; + private readonly Mock _mockEvaluationDetailService; + private readonly IComplexEvaluationDetailService _service; + + public ComplexEvaluationDetailServiceTest() + { + _mockRateEvaluationService = new Mock(); + _mockEvaluationDetailService = new Mock(); + _service = new ComplexEvaluationDetailService(_mockRateEvaluationService.Object, + _mockEvaluationDetailService.Object); + } + + [Fact] + public async void ComplexEvaluationDetail_UpdateComplexAsync_Success() + { + var evaluationDetail = new EvaluationDetail + { + EvaluationDetailId = DateTime.Now.Millisecond, + Description = Guid.NewGuid().ToString() + }; + + var newEvaluationDetail = new EvaluationDetail + { + EvaluationDetailId = DateTime.Now.Millisecond, + Description = Guid.NewGuid().ToString() + }; + + _mockEvaluationDetailService.Setup(x => x.UpdateComplexAsync(It.IsAny())) + .ReturnsAsync(newEvaluationDetail).Verifiable(); + + var result = await _service.UpdateComplexAsync(evaluationDetail); + Assert.NotNull(result); + Assert.Equal(result.Description, newEvaluationDetail.Description); + _mockEvaluationDetailService.Verify(x => x.UpdateComplexAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async void ComplexEvaluationDetail_UpdateComplexAsync_Success_NotFound() + { + EvaluationDetail? evaluationDetail = null; + + _mockEvaluationDetailService.Setup(x => x.UpdateComplexAsync(It.IsAny())) + .ReturnsAsync(evaluationDetail).Verifiable(); + + var result = await _service.UpdateComplexAsync(evaluationDetail); + Assert.Null(result); + _mockEvaluationDetailService.Verify(x => x.UpdateComplexAsync(It.IsAny()), Times.Once); + } + + [Fact] + public void ComplexEvaluationDetail_UpdateComplexAsync_Failure_Exception() + { + var evaluationDetail = new EvaluationDetail(); + _mockEvaluationDetailService.Setup(x => x.UpdateComplexAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.UpdateComplexAsync(evaluationDetail)); + _mockEvaluationDetailService.Verify(x => x.UpdateComplexAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async void ComplexEvaluationDetail_SaveComplexAsync_Success_NewEvaluationDetail() + { + var evaluationDetail = new EvaluationDetail + { + Description = Guid.NewGuid().ToString() + }; + + var request = new EvaluationDetailRequest + { + EvaluationDetail = evaluationDetail + }; + + var newEvaluationDetail = new EvaluationDetail + { + EvaluationDetailId = DateTime.Now.Millisecond + 1, + Description = evaluationDetail.Description + }; + + _mockEvaluationDetailService.Setup(x => x.SaveComplexAsync(It.IsAny())) + .ReturnsAsync(newEvaluationDetail).Verifiable(); + + var result = await _service.SaveComplexAsync(request); + Assert.NotNull(result); + Assert.Equal(request.EvaluationDetail.Description, newEvaluationDetail.Description); + _mockEvaluationDetailService.Verify(x => x.SaveComplexAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async void ComplexEvaluationDetail_SaveComplexAsync_Success_ExistingEvaluationDetail() + { + var evaluationDetail = new EvaluationDetail + { + EvaluationDetailId = DateTime.Now.Millisecond, + Description = Guid.NewGuid().ToString() + }; + + var rateEvaluations = new List + { + new() + { + EvaluationDetailId = evaluationDetail.EvaluationDetailId, + RateId = DateTime.Now.Millisecond + 5 + } + }; + + var request = new EvaluationDetailRequest + { + PreviousVersionEvaluationDetailId = evaluationDetail.EvaluationDetailId, + EvaluationDetail = evaluationDetail + }; + + var newEvaluationDetail = new EvaluationDetail + { + EvaluationDetailId = DateTime.Now.Millisecond + 1, + Description = evaluationDetail.Description + }; + + _mockEvaluationDetailService.Setup(x => x.SaveComplexAsync(It.IsAny())) + .ReturnsAsync(newEvaluationDetail).Verifiable(); + _mockRateEvaluationService.Setup(x => x.GetRangeByEvaluationDetailIdAsync(It.IsAny())) + .ReturnsAsync(rateEvaluations).Verifiable(); + _mockRateEvaluationService.Setup(x => x.SaveRangeAsync(rateEvaluations)).Verifiable(); + + var result = await _service.SaveComplexAsync(request); + Assert.NotNull(result); + Assert.Equal(request.EvaluationDetail.Description, newEvaluationDetail.Description); + _mockEvaluationDetailService.Verify(x => x.SaveComplexAsync(It.IsAny()), Times.Once); + _mockRateEvaluationService.Verify(x => x.GetRangeByEvaluationDetailIdAsync(It.IsAny()), Times.Once); + _mockRateEvaluationService.Verify(x => x.SaveRangeAsync(It.IsAny>()), Times.Once); + } + + [Fact] + public async void ComplexEvaluationDetail_SaveComplexAsync_Success_NotFound() + { + var request = new EvaluationDetailRequest(); + EvaluationDetail? evaluationDetail = null; + + _mockEvaluationDetailService.Setup(x => x.SaveComplexAsync(It.IsAny())) + .ReturnsAsync(evaluationDetail).Verifiable(); + + var result = await _service.SaveComplexAsync(request); + Assert.Null(result); + _mockEvaluationDetailService.Verify(x => x.SaveComplexAsync(It.IsAny()), Times.Once); + } + + [Fact] + public void ComplexEvaluationDetail_SaveComplexAsync_Failure_Exception() + { + var request = new EvaluationDetailRequest(); + _mockEvaluationDetailService.Setup(x => x.SaveComplexAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.SaveComplexAsync(request)); + _mockEvaluationDetailService.Verify(x => x.SaveComplexAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async void ComplexEvaluationDetail_DeleteComplexAsync_Success() + { + var evaluationDetailId = DateTime.Now.Millisecond; + + _mockEvaluationDetailService.Setup(x => x.DeleteComplexAsync(It.IsAny())).Verifiable(); + _mockRateEvaluationService.Setup(x => x.DeleteRangeByEvaluationDetailIdAsync(It.IsAny())).Verifiable(); + + await _service.DeleteComplexAsync(evaluationDetailId); + _mockEvaluationDetailService.Verify(x => x.DeleteComplexAsync(It.IsAny()), Times.Once); + _mockRateEvaluationService.Verify(x => x.DeleteRangeByEvaluationDetailIdAsync(It.IsAny()), Times.Once); + } + + [Fact] + public void ComplexEvaluationDetail_DeleteComplexAsync_Failure_Exception() + { + _mockEvaluationDetailService.Setup(x => x.DeleteComplexAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.DeleteComplexAsync(It.IsAny())); + _mockEvaluationDetailService.Verify(x => x.DeleteComplexAsync(It.IsAny()), Times.Once); + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestRunInfoServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestRunInfoServiceTest.cs new file mode 100644 index 0000000..e9e4d23 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestRunInfoServiceTest.cs @@ -0,0 +1,586 @@ +using LCU.API.Internal.LoanRate.Services; +using LCU.API.Internal.LoanRateLoadTestingLib.Repositories; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LCUFilterModels.Pagination; +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests; + +public class LoadTestRunInfoServiceTest +{ + private readonly Mock _mockUnitOfWork; + private readonly Mock _mockLoadTestRunInfoRepository; + private readonly ILoadTestRunInfoService _loadTestRunInfoService; + + public LoadTestRunInfoServiceTest() + { + _mockUnitOfWork = new Mock(); + _mockLoadTestRunInfoRepository = new Mock(); + _loadTestRunInfoService = new LoadTestRunInfoService(_mockUnitOfWork.Object, + _mockLoadTestRunInfoRepository.Object); + } + + [Fact] + public async void LoadTestRunInfo_GetAsync_Success() + { + var loadTestRunInfos = new List + { + new() { LoadTestRunInfoId = 1 }, + new() { LoadTestRunInfoId = 2 }, + new() { LoadTestRunInfoId = 3 } + }; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindAsync()) + .ReturnsAsync(loadTestRunInfos).Verifiable(); + + var result = (await _loadTestRunInfoService.GetAsync()).ToList(); + Assert.NotNull(result); + Assert.Equal(loadTestRunInfos.First().LoadTestRunInfoId, result.First().LoadTestRunInfoId); + Assert.Equal(loadTestRunInfos.Count, result.Count); + _mockLoadTestRunInfoRepository.Verify(); + } + + [Fact] + public async void LoadTestRunInfo_GetAsync_Success_NotFound() + { + List? loadTestRunInfos = null; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindAsync()) + .ReturnsAsync(loadTestRunInfos).Verifiable(); + + var result = await _loadTestRunInfoService.GetAsync(); + Assert.NotNull(result); + Assert.Empty(result); + _mockLoadTestRunInfoRepository.Verify(); + } + + [Fact] + public void LoadTestRunInfo_GetAsync_Failure_Exception() + { + _mockLoadTestRunInfoRepository.Setup(x => x.FindAsync()) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _loadTestRunInfoService.GetAsync()); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindAsync(), Times.Once); + } + + [Fact] + public async void LoadTestRunInfo_GetByIdAsync_Success() + { + var loadTestRunInfoId = 1; + var loadTestRunInfo = new LoadTestRunInfo + { + LoadTestRunInfoId = loadTestRunInfoId + }; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(loadTestRunInfoId)) + .ReturnsAsync(loadTestRunInfo).Verifiable(); + + var result = await _loadTestRunInfoService.GetByIdAsync(loadTestRunInfoId); + Assert.NotNull(result); + Assert.Equal(loadTestRunInfo.LoadTestRunInfoId, result.LoadTestRunInfoId); + _mockLoadTestRunInfoRepository.Verify(); + } + + [Fact] + public async void LoadTestRunInfo_GetByIdAsync_Success_NotFound() + { + var loadTestRunInfoId = 1; + LoadTestRunInfo? loadTestRunInfo = null; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(loadTestRunInfoId)) + .ReturnsAsync(loadTestRunInfo).Verifiable(); + + var result = await _loadTestRunInfoService.GetByIdAsync(loadTestRunInfoId); + Assert.Null(result); + _mockLoadTestRunInfoRepository.Verify(); + } + + [Fact] + public void LoadTestRunInfo_GetByIdAsync_Failure_Exception() + { + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _loadTestRunInfoService.GetByIdAsync(It.IsAny())); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async void LoadTestRunInfo_GetRangeByIdAsync_Success() + { + var loadTestRunInfos = new List + { + new() { LoadTestRunInfoId = 1 }, + new() { LoadTestRunInfoId = 2 }, + new() { LoadTestRunInfoId = 3 } + }; + var ids = loadTestRunInfos.Select(x => x.LoadTestRunInfoId).ToList(); + + _mockLoadTestRunInfoRepository.Setup(x => x.FindRangeByIdAsync(ids)) + .ReturnsAsync(loadTestRunInfos).Verifiable(); + + var results = (await _loadTestRunInfoService.GetRangeByIdAsync(ids)).ToList(); + Assert.NotNull(results); + Assert.Equal(loadTestRunInfos.First().LoadTestRunInfoId, results.First().LoadTestRunInfoId); + Assert.Equal(loadTestRunInfos.Count, results.Count); + _mockLoadTestRunInfoRepository.Verify(); + } + + [Fact] + public async void LoadTestRunInfo_GetRangeByIdAsync_Success_NotFound() + { + List? loadTestRunInfos = null; + var ids = new List + { + 1,2,3 + }; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindRangeByIdAsync(ids)) + .ReturnsAsync(loadTestRunInfos).Verifiable(); + + var result = await _loadTestRunInfoService.GetRangeByIdAsync(ids); + Assert.NotNull(result); + Assert.Empty(result); + _mockLoadTestRunInfoRepository.Verify(); + } + + [Fact] + public void LoadTestRunInfo_GetRangeByIdAsync_Failure_Exception() + { + _mockLoadTestRunInfoRepository.Setup(x => x.FindRangeByIdAsync(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _loadTestRunInfoService.GetRangeByIdAsync(It.IsAny>())); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once); + } + + [Fact] + public async Task LoadTestRunInfo_GetFilteredLoadTestRunInfo_Success() + { + var pagedLoadTestRunInfoResult = new PagedResult(); + + var filter = new LoadTestRunInfoFilter + { + Page = 1, + Limit = 2, + SortableFields = null, + RateTypes = new List { RateTypes.DirectAuto } + }; + + _mockLoadTestRunInfoRepository.Setup(repo => repo.FindFilteredLoadTestRunInfosAsync(filter)) + .ReturnsAsync(pagedLoadTestRunInfoResult).Verifiable(); + + var result = await _loadTestRunInfoService.GetFilteredLoadTestRunInfo(filter); + + Assert.NotNull(result); + Assert.Equal(pagedLoadTestRunInfoResult, result); + _mockLoadTestRunInfoRepository.Verify(x => x.FindFilteredLoadTestRunInfosAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task LoadTestRunInfo_GetFilteredLoadTestRunInfo_NotFound() + { + var pagedLoadTestRunInfoResult = new PagedResult + { + Total = 0, + Items = new List() + }; + + var filter = new LoadTestRunInfoFilter + { + Page = 1, + Limit = 2, + SortableFields = null, + RateTypes = new List { RateTypes.DirectAuto } + }; + + _mockLoadTestRunInfoRepository.Setup(repo => repo.FindFilteredLoadTestRunInfosAsync(filter)) + .ReturnsAsync(pagedLoadTestRunInfoResult).Verifiable(); + + var result = await _loadTestRunInfoService.GetFilteredLoadTestRunInfo(filter); + + Assert.NotNull(result); + Assert.Equal(0, result.Total); + _mockLoadTestRunInfoRepository.Verify(x => x.FindFilteredLoadTestRunInfosAsync(It.IsAny()), Times.Once); + } + + [Fact] + public void LoadTestRunInfo_GetFilteredLoadTestRunInfo_Failure_Exception() + { + _mockLoadTestRunInfoRepository.Setup(x => x.FindFilteredLoadTestRunInfosAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _loadTestRunInfoService.GetFilteredLoadTestRunInfo(It.IsAny())); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindFilteredLoadTestRunInfosAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async void LoadTestRunInfo_SaveAsync_Success() + { + var loadTestRunInfo = new LoadTestRunInfo + { + RateType = RateTypes.DirectBoat + }; + var LoadTestRunInfoReturned = new LoadTestRunInfo + { + LoadTestRunInfoId = 1, + RateType = RateTypes.DirectPowerSport + }; + + _mockLoadTestRunInfoRepository.Setup(x => x.AddAsync(loadTestRunInfo)) + .ReturnsAsync(LoadTestRunInfoReturned).Verifiable(); + + var result = await _loadTestRunInfoService.SaveAsync(loadTestRunInfo); + Assert.NotNull(result); + Assert.Equal(LoadTestRunInfoReturned.LoadTestRunInfoId, result.LoadTestRunInfoId); + Assert.Equal(LoadTestRunInfoReturned.RateType, result.RateType); + + _mockLoadTestRunInfoRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void LoadTestRunInfo_SaveAsync_Failure_ReturnedNull() + { + var loadTestRunInfo = new LoadTestRunInfo + { + RateType = RateTypes.DirectSignatureLoan + }; + LoadTestRunInfo? loadTestRunInfoReturned = null; + + _mockLoadTestRunInfoRepository.Setup(x => x.AddAsync(loadTestRunInfo)) + .ReturnsAsync(loadTestRunInfoReturned).Verifiable(); + + Assert.ThrowsAsync(async () => await _loadTestRunInfoService.SaveAsync(loadTestRunInfo)); + + _mockLoadTestRunInfoRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public void LoadTestRunInfo_SaveAsync_Failure_Exception() + { + _mockLoadTestRunInfoRepository.Setup(x => x.AddAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _loadTestRunInfoService.SaveAsync(It.IsAny())); + + _mockLoadTestRunInfoRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public async void LoadTestRunInfo_SaveRangeAsync_Success() + { + var loadTestRunInfos = new List + { + new() { RateType = RateTypes.DirectAuto }, + new() { RateType = RateTypes.DirectBoat }, + new() { RateType = RateTypes.DirectRv } + }; + + _mockLoadTestRunInfoRepository.Setup(x => x.AddRangeAsync(loadTestRunInfos)).Verifiable(); + + await _loadTestRunInfoService.SaveRangeAsync(loadTestRunInfos); + _mockLoadTestRunInfoRepository.Verify(x => + x.AddRangeAsync(It.IsAny>()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void LoadTestRunInfo_SaveRangeAsync_Failure_Exception() + { + _mockLoadTestRunInfoRepository.Setup(x => x.AddRangeAsync(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _loadTestRunInfoService.SaveRangeAsync(It.IsAny>())); + + _mockLoadTestRunInfoRepository.Verify(x => + x.AddRangeAsync(It.IsAny>()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public async void LoadTestRunInfo_IncreaseRateProcessingInfosCompleteCountAsync_Success() + { + var random = new Random(); + var previousComplete = random.Next(1, 100); + var countCompleted = random.Next(1, 100); + var loadTestRunInfo = new LoadTestRunInfo + { + LoadTestRunInfoId = DateTime.Now.Millisecond, + RateProcessingInfosCompleteCount = previousComplete, + RateProcessingInfosCount = previousComplete + countCompleted + }; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(loadTestRunInfo.LoadTestRunInfoId)).ReturnsAsync(loadTestRunInfo).Verifiable(); + + var result = await _loadTestRunInfoService.IncreaseRateProcessingInfosCompleteCountAsync(loadTestRunInfo.LoadTestRunInfoId, countCompleted); + + Assert.NotNull(result); + Assert.Equal(previousComplete + countCompleted, result.RateProcessingInfosCompleteCount); + Assert.Equal(DateTime.Now.Date, ((DateTime)result.EndTimeStamp!).Date); + _mockLoadTestRunInfoRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public async void LoadTestRunInfo_IncreaseRateProcessingInfosCompleteCountAsync_Failure_ReturnedNull() + { + var loadTestRuninfoId = new Random().Next(1, 100); + LoadTestRunInfo? loadTestRunInfo = null; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(loadTestRuninfoId)).ReturnsAsync(loadTestRunInfo).Verifiable(); + + var result = await _loadTestRunInfoService.IncreaseRateProcessingInfosCompleteCountAsync(loadTestRuninfoId, 10); + + Assert.Null(result); + _mockLoadTestRunInfoRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + [Fact] + public void LoadTestRunInfo_IncreaseRateProcessingInfosCompleteCountAsync_Failure_Exception() + { + var random = new Random(); + var previousComplete = random.Next(1, 100); + var countCompleted = random.Next(1, 100); + var loadTestRunInfo = new LoadTestRunInfo + { + LoadTestRunInfoId = DateTime.Now.Millisecond, + RateProcessingInfosCompleteCount = previousComplete, + RateProcessingInfosCount = previousComplete + countCompleted + }; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(loadTestRunInfo.LoadTestRunInfoId)) + .ThrowsAsync(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync( async () => + await _loadTestRunInfoService.IncreaseRateProcessingInfosCompleteCountAsync(loadTestRunInfo.LoadTestRunInfoId, countCompleted)); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + + [Fact] + public async void LoadTestRunInfo_UpdateAsync_Success() + { + var loadTestRunInfo = new LoadTestRunInfo + { + LoadTestRunInfoId = 1, + RateType = RateTypes.DirectAuto + }; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(loadTestRunInfo.LoadTestRunInfoId)) + .ReturnsAsync(loadTestRunInfo).Verifiable(); + + var result = await _loadTestRunInfoService.UpdateAsync(loadTestRunInfo); + Assert.NotNull(result); + Assert.Equal(loadTestRunInfo.LoadTestRunInfoId, result.LoadTestRunInfoId); + Assert.Equal(loadTestRunInfo.RateType, result.RateType); + _mockLoadTestRunInfoRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void LoadTestRunInfo_UpdateAsync_Failure_ReturnedNull() + { + var loadTestRunInfo = new LoadTestRunInfo + { + RateType = RateTypes.DirectAuto + }; + LoadTestRunInfo? loadTestRunInfoReturned = null; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(loadTestRunInfo.LoadTestRunInfoId)) + .ReturnsAsync(loadTestRunInfoReturned).Verifiable(); + + Assert.ThrowsAsync(async () => await _loadTestRunInfoService.UpdateAsync(loadTestRunInfo)); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public void LoadTestRunInfo_UpdateAsync_Failure_Exception() + { + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(It.IsAny())) + .ThrowsAsync(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _loadTestRunInfoService.UpdateAsync(It.IsAny())); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public async void LoadTestRunInfo_UpdateRangeAsync_Success() + { + var loadTestRunInfos = new List + { + new() + { + LoadTestRunInfoId = 1 + }, + new() + { + LoadTestRunInfoId = 2 + }, + new() + { + LoadTestRunInfoId = 3 + } + }; + + _mockLoadTestRunInfoRepository.Setup(x => x.UpdateRange(loadTestRunInfos)).Verifiable(); + + await _loadTestRunInfoService.UpdateRangeAsync(loadTestRunInfos); + + _mockLoadTestRunInfoRepository.Verify(x => x.UpdateRange(It.IsAny>()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void LoadTestRunInfo_UpdateRangeAsync_Failure_Exception() + { + _mockLoadTestRunInfoRepository.Setup(x => x.UpdateRange(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _loadTestRunInfoService.UpdateRangeAsync(It.IsAny>())); + + _mockLoadTestRunInfoRepository.Verify(x => x.UpdateRange(It.IsAny>()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public async void LoadTestRunInfo_DeleteAsync_Success() + { + var loadTestRunInfoId = 1; + + var loadTestRunInfo = new LoadTestRunInfo + { + LoadTestRunInfoId = loadTestRunInfoId + }; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(loadTestRunInfoId)) + .ReturnsAsync(loadTestRunInfo).Verifiable(); + _mockLoadTestRunInfoRepository.Setup(x => x.Remove(loadTestRunInfo)).Verifiable(); + + await _loadTestRunInfoService.DeleteAsync(loadTestRunInfoId); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once()); + _mockLoadTestRunInfoRepository.Verify(x => x.Remove(It.IsAny()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void LoadTestRunInfo_DeleteAsync_Failure_ReturnedNull() + { + var loadTestRunInfoId = 1; + LoadTestRunInfo? loadTestRunInfo = null; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(loadTestRunInfoId)) + .ReturnsAsync(loadTestRunInfo).Verifiable(); + + Assert.ThrowsAsync(async () => await _loadTestRunInfoService.DeleteAsync(loadTestRunInfoId)); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once()); + _mockLoadTestRunInfoRepository.Verify(x => x.Remove(It.IsAny()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public void LoadTestRunInfo_DeleteAsync_Failure_Exception() + { + _mockLoadTestRunInfoRepository.Setup(x => x.FindByIdAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _loadTestRunInfoService.DeleteAsync(It.IsAny())); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + _mockLoadTestRunInfoRepository.Verify(x => x.Remove(It.IsAny()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public async void LoadTestRunInfo_DeleteRangeAsync_Success() + { + var loadTestRunInfoIds = new List + { + 1, + 2, + 3 + }; + var loadTestRunInfos = new List + { + new() + { + LoadTestRunInfoId = loadTestRunInfoIds[0] + + }, + new() + { + LoadTestRunInfoId = loadTestRunInfoIds[1] + }, + new() + { + LoadTestRunInfoId = loadTestRunInfoIds[2] + } + }; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindRangeByIdAsync(loadTestRunInfoIds)).ReturnsAsync(loadTestRunInfos).Verifiable(); + _mockLoadTestRunInfoRepository.Setup(x => x.RemoveRange(loadTestRunInfos)).Verifiable(); + + await _loadTestRunInfoService.DeleteRangeAsync(loadTestRunInfoIds); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once()); + _mockLoadTestRunInfoRepository.Verify(x => x.RemoveRange(It.IsAny>()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void LoadTestRunInfo_DeleteRangeAsync_Failure_ReturnedNull() + { + var loadTestRunInfoIds = new List + { + 1, + 2, + 3 + }; + List? loadTestRunInfos = null; + + _mockLoadTestRunInfoRepository.Setup(x => x.FindRangeByIdAsync(loadTestRunInfoIds)) + .ReturnsAsync(loadTestRunInfos).Verifiable(); + + Assert.ThrowsAsync(async () => await _loadTestRunInfoService.DeleteRangeAsync(loadTestRunInfoIds)); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once()); + _mockLoadTestRunInfoRepository.Verify(x => x.RemoveRange(It.IsAny>()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public void LoadTestRunInfo_DeleteRangeAsync_Failure_Exception() + { + _mockLoadTestRunInfoRepository.Setup(x => x.RemoveRange(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _loadTestRunInfoService.DeleteRangeAsync(It.IsAny>())); + + _mockLoadTestRunInfoRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once()); + _mockLoadTestRunInfoRepository.Verify(x => x.RemoveRange(It.IsAny>()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestingServices/LoadTestInitiationServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestingServices/LoadTestInitiationServiceTest.cs new file mode 100644 index 0000000..81a87c0 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestingServices/LoadTestInitiationServiceTest.cs @@ -0,0 +1,127 @@ +using System.Globalization; +using System.Reflection; +using LCU.API.Internal.LoanRate.Services; +using LCU.API.Internal.LoanRate.Services.LoadTestingServices; +using LCU.API.Internal.LoanRate.Services.QueueServices; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +using LCU.Library.Internal.LoanRateModels.Enums; +using LCU.Library.Internal.LoanRateModels.Enums.LoadTestingEnums; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +using Newtonsoft.Json; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests.LoadTestingServices; + +public class LoadTestInitiationServiceTest +{ + private readonly Mock _mockLoadTestRunInfoService; + private readonly Mock _mockRateProcessingInfoService; + private readonly Mock _mockQueueLoadTestProcessRateService; + private readonly ILoadTestInitiationService _loadTestInitiationService; + + public LoadTestInitiationServiceTest() + { + _mockLoadTestRunInfoService = new Mock(); + _mockRateProcessingInfoService = new Mock(); + _mockQueueLoadTestProcessRateService = new Mock(); + _loadTestInitiationService = new LoadTestInitiationService(_mockLoadTestRunInfoService.Object, + _mockRateProcessingInfoService.Object, + _mockQueueLoadTestProcessRateService.Object); + } + + [Fact] + public async void HandleLoadTestInitiation_Success() + { + var propertyCount = new Random().Next(1, 100); + var request = GenerateLoadTestInitiateRequest(propertyCount); + var loadTestRunInfo = new LoadTestRunInfo { LoadTestRunInfoId = DateTime.Now.Millisecond }; + var rateProcessingInfos = GenerateRateProcessingInfos(request, loadTestRunInfo.LoadTestRunInfoId).ToList(); + + _mockLoadTestRunInfoService.Setup(x => x.SaveAsync(It.IsAny())).ReturnsAsync(loadTestRunInfo).Verifiable(); + _mockRateProcessingInfoService.Setup(x => x.SaveRangeAsync(It.Is>( + list => list.All( + rateProcessingInfo => rateProcessingInfo.LoadTestRunInfoId == loadTestRunInfo.LoadTestRunInfoId + && !string.IsNullOrWhiteSpace(rateProcessingInfo.Payload) + && rateProcessingInfo.RateProcessingStatusType == RateProcessingStatusTypes.Processing) + && list.Count == propertyCount))).Verifiable(); + _mockRateProcessingInfoService.Setup(x => x.GetByLoadTestRunInfoId(loadTestRunInfo.LoadTestRunInfoId)).ReturnsAsync(rateProcessingInfos).Verifiable(); + _mockQueueLoadTestProcessRateService.Setup(x => x.QueueLoadTestProcessRateRequest(It.Is( loadTestRequest => + loadTestRequest.RateProcessingInfo.LoadTestRunInfoId == loadTestRunInfo.LoadTestRunInfoId ))).Verifiable(); + + await _loadTestInitiationService.HandleLoadTestInitiation(request); + + _mockLoadTestRunInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Once); + _mockRateProcessingInfoService.Verify(x => x.SaveRangeAsync(It.IsAny>()), Times.Once); + _mockRateProcessingInfoService.Verify(x => x.GetByLoadTestRunInfoId(It.IsAny()), Times.Once); + _mockQueueLoadTestProcessRateService.Verify(x => x.QueueLoadTestProcessRateRequest(It.IsAny()), Times.Exactly(rateProcessingInfos.Count)); + } + + [Fact] + public void HandleLoadTestInitiation_Failure_ThrowsException() + { + var propertyCount = new Random().Next(1, 100); + var request = GenerateLoadTestInitiateRequest(propertyCount); + + _mockLoadTestRunInfoService.Setup(x => x.SaveAsync(It.IsAny())).ThrowsAsync(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync( async () => await _loadTestInitiationService.HandleLoadTestInitiation(request)); + + _mockLoadTestRunInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Once); + _mockRateProcessingInfoService.Verify(x => x.SaveRangeAsync(It.IsAny>()), Times.Never); + _mockRateProcessingInfoService.Verify(x => x.GetByLoadTestRunInfoId(It.IsAny()), Times.Never); + _mockQueueLoadTestProcessRateService.Verify(x => x.QueueLoadTestProcessRateRequest(It.IsAny()), Times.Never); + } + + private LoadTestInitiateRequest GenerateLoadTestInitiateRequest(int propertyListLength = 1) + { + var values = Enum.GetValues(typeof(RateTypes)); + var random = new Random(); + var rateType = (RateTypes)(values.GetValue(random.Next(values.Length)) ?? RateTypes.DirectAuto); + + var propertyList = new Dictionary>(); + var properties = typeof(RateRequest).GetProperties(); + var availableProperties = properties.Except(new List {properties.First(x => x.Name == nameof(RateRequest.RateType))}).ToList(); + for (var i = 0; i < propertyListLength; i++) + { + var key = availableProperties[random.Next(availableProperties.Count)]; + string value; + if (key.PropertyType == typeof(DateTime)) + { + value = new DateTime().AddDays(random.Next(1000)).ToString(CultureInfo.InvariantCulture); + } + else if (new List {typeof(int), typeof(decimal), typeof(long)}.Contains(key.PropertyType)) + { + value = random.Next(100).ToString(); + } + else + { + value = Guid.NewGuid().ToString(); + } + + if (!propertyList.ContainsKey(key.Name)) + { + propertyList.Add(key.Name, new List { value } ); + } + else + { + propertyList[key.Name].Add(value); + } + } + + return new LoadTestInitiateRequest + { + RateType = rateType, + PropertyList = propertyList + }; + } + + private static IEnumerable GenerateRateProcessingInfos(LoadTestInitiateRequest request, int loadTestRunInfoId) + => request.PropertyList.Select( + combination => new RateProcessingInfo + { + LoadTestRunInfoId = loadTestRunInfoId, + RateProcessingStatusType = RateProcessingStatusTypes.Processing, + Payload = JsonConvert.SerializeObject(combination) + }).ToList(); +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestingServices/LoadTestProcessRateServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestingServices/LoadTestProcessRateServiceTest.cs new file mode 100644 index 0000000..96abe89 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestingServices/LoadTestProcessRateServiceTest.cs @@ -0,0 +1,73 @@ +using LCU.API.Internal.LoanRate.Services.LoadTestingServices; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +using LCU.API.Internal.LoanRate.Services.QueueServices; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LcuEvaluationModels.Entities; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; +using Newtonsoft.Json; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests.LoadTestingServices; + +public class LoadTestProcessRateServiceTest +{ + private readonly Mock _mockRateFinderService; + private readonly Mock _mockQueueLoadTestRateCompletionService; + private readonly ILoadTestProcessRateService _loadTestProcessRateService; + + public LoadTestProcessRateServiceTest() + { + _mockRateFinderService = new Mock(); + _mockQueueLoadTestRateCompletionService = new Mock(); + _loadTestProcessRateService = new LoadTestProcessRateService(_mockRateFinderService.Object, + _mockQueueLoadTestRateCompletionService.Object); + } + + [Fact] + public async void HandleLoadTestProcessRate_Success() + { + var random = new Random(); + var request = new LoadTestProcessRateRequest { + RateProcessingInfo = new RateProcessingInfo + { + LoadTestRunInfoId = random.Next(1, 1000), + RateProcessingInfoId = random.Next(1, 1000), + Payload = JsonConvert.SerializeObject(new RateRequest()) + } + }; + + _mockRateFinderService.Setup(x => x.DetermineRate(It.IsAny(), It.IsAny>())).ReturnsAsync(( new Rate(), string.Empty )); + + await _loadTestProcessRateService.HandleLoadTestProcessRate(request); + + _mockRateFinderService.Verify(x => x.DeterminePassedRules(It.IsAny()), Times.Once); + _mockRateFinderService.Verify(x => x.DetermineRate(It.IsAny(), It.IsAny>()), Times.Once); + _mockRateFinderService.Verify(x => x.CalculateFinalRateValue(It.IsAny(), It.IsAny()), Times.Once); + _mockQueueLoadTestRateCompletionService.Verify(x => x.QueueLoadTestRateCompletionRequest(It.IsAny()), Times.Once); + } + + [Fact] + public void HandleLoadTestProcessRate_Failure_ThrowsException() + { + var random = new Random(); + var request = new LoadTestProcessRateRequest { + RateProcessingInfo = new RateProcessingInfo + { + LoadTestRunInfoId = random.Next(1, 1000), + RateProcessingInfoId = random.Next(1, 1000), + Payload = JsonConvert.SerializeObject(new RateRequest()) + } + }; + + _mockRateFinderService.Setup(x => x.DeterminePassedRules(It.IsAny())).ThrowsAsync(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync( async () => await _loadTestProcessRateService.HandleLoadTestProcessRate(request)); + + _mockRateFinderService.Verify(x => x.DeterminePassedRules(It.IsAny()), Times.Once); + _mockRateFinderService.Verify(x => x.DetermineRate(It.IsAny(), It.IsAny>()), Times.Never); + _mockRateFinderService.Verify(x => x.CalculateFinalRateValue(It.IsAny(), It.IsAny()), Times.Never); + _mockQueueLoadTestRateCompletionService.Verify(x => x.QueueLoadTestRateCompletionRequest(It.IsAny()), Times.Once); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestingServices/LoadTestRateCompletionServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestingServices/LoadTestRateCompletionServiceTest.cs new file mode 100644 index 0000000..9ad6e7b --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoadTestingServices/LoadTestRateCompletionServiceTest.cs @@ -0,0 +1,76 @@ +using LCU.API.Internal.LoanRate.Services; +using LCU.API.Internal.LoanRate.Services.LoadTestingServices; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +using LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests.LoadTestingServices; + +public class LoadTestRateCompletionServiceTest +{ + private readonly Mock _mockRateProcessingInfoService; + private readonly Mock _mockLoadTestRunInfoService; + private readonly ILoadTestRateCompletionService _loadTestRateCompletionService; + + public LoadTestRateCompletionServiceTest() + { + _mockRateProcessingInfoService = new Mock(); + _mockLoadTestRunInfoService = new Mock(); + _loadTestRateCompletionService = new LoadTestRateCompletionService(_mockRateProcessingInfoService.Object, + _mockLoadTestRunInfoService.Object); + } + + [Fact] + public async void HandleLoadTestRateCompletion_Success() + { + var random = new Random(); + var requestCount = random.Next(1, 100); + var requests = GenerateLoadTestRateCompletionRequests(random.Next(1, requestCount), requestCount); + var rateProcessingInfos = requests.Select(x => x.RateProcessingInfo).ToList(); + var loadTestRunInfoGroups = rateProcessingInfos.GroupBy( p => p.LoadTestRunInfoId, p => p.RateProcessingInfoId); + + await _loadTestRateCompletionService.HandleLoadTestRateCompletion(requests); + + _mockRateProcessingInfoService.Verify(x => x.UpdateRangeAsync(It.IsAny>()), Times.Once); + _mockLoadTestRunInfoService.Verify(x => x.IncreaseRateProcessingInfosCompleteCountAsync(It.IsAny(), It.IsAny()), Times.Exactly(loadTestRunInfoGroups.Count())); + } + + [Fact] + public void HandleLoadTestRateCompletion_Failure_ThrowsException() + { + var requests = GenerateLoadTestRateCompletionRequests(1, 2); + + _mockRateProcessingInfoService.Setup(x => x.UpdateRangeAsync(It.IsAny>())).ThrowsAsync(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync( async () => await _loadTestRateCompletionService.HandleLoadTestRateCompletion(requests)); + + _mockRateProcessingInfoService.Verify(x => x.UpdateRangeAsync(It.IsAny>()), Times.Once); + _mockLoadTestRunInfoService.Verify(x => x.IncreaseRateProcessingInfosCompleteCountAsync(It.IsAny(), It.IsAny()), Times.Never); + } + + private List GenerateLoadTestRateCompletionRequests(int loadTestCount, int rateProcessingInfoCount) + { + var random = new Random(); + + var loadTestIds = new List(); + for (var i = 0; i < loadTestCount; i++) + { + loadTestIds.Add(random.Next(1, int.MaxValue)); + } + + var requests = new List(); + for (var i = 0; i < rateProcessingInfoCount; i++) + { + requests.Add(new LoadTestRateCompletionRequest + { + RateProcessingInfo = new RateProcessingInfo + { + LoadTestRunInfoId = loadTestIds[random.Next(0, loadTestCount - 1)], + RateProcessingInfoId = random.Next(1, int.MaxValue) + } + }); + } + + return requests; + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoanRateOperationServicesTests/RateFinderServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoanRateOperationServicesTests/RateFinderServiceTest.cs new file mode 100644 index 0000000..08d4a1d --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoanRateOperationServicesTests/RateFinderServiceTest.cs @@ -0,0 +1,382 @@ +using LCU.Library.Internal.LcuEvaluation.Services; +using LCU.Library.Internal.LcuEvaluationModels.Entities; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.LcuEvaluation.Services.EvaluationServices; +using LCU.Library.Internal.LcuEvaluationModels.Enums; +using LCU.Library.Internal.LcuEvaluationModels.Requests; +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests.LoanRateOperationServicesTests; + +public class RateFinderServiceTest +{ + private readonly RateFinderService _rateFinderService; + private readonly Mock _mockEvaluationDetailService; + private readonly Mock _mockRateService; + private readonly Mock _mockRateSheetService; + private readonly Mock _mockRateEvaluationService; + private readonly Mock _mockRunEvaluationService; + private readonly Mock _mockRunCalculationService; + + public RateFinderServiceTest() + { + _mockEvaluationDetailService = new Mock(); + _mockRateService = new Mock(); + _mockRateSheetService = new Mock(); + _mockRateEvaluationService = new Mock(); + _mockRunEvaluationService = new Mock(); + _mockRunCalculationService = new Mock(); + _rateFinderService = new RateFinderService(_mockEvaluationDetailService.Object, + _mockRateService.Object, + _mockRateSheetService.Object, + _mockRateEvaluationService.Object, + _mockRunEvaluationService.Object, + _mockRunCalculationService.Object); + } + + [Fact] + public void FindRate_NoEvaluationsReturned_Exception() + { + var rateRequest = new RateRequest { ApplicationDate = DateTime.Now}; + var rateSheetIds = new List { 1, 2, 3 }; + _mockRateSheetService.Setup(x => x.GetRateSheetIdsByRateTypeAsync(RateTypes.DirectBoat)).ReturnsAsync(rateSheetIds); + _mockRateEvaluationService.Setup(x => x.GetByRateSheetIdsAsync(rateSheetIds)).ReturnsAsync(new List()); + _mockEvaluationDetailService.Setup(x => x.GetActiveByEvaluationTypeAndIdsAsync(rateRequest.ApplicationDate, EvaluationTypes.Rule, new List())).ReturnsAsync(new List()).Verifiable(); + + Assert.ThrowsAsync(async () => await _rateFinderService.FindRate(rateRequest)); + + _mockRateSheetService.Verify(x => x.GetRateSheetIdsByRateTypeAsync(RateTypes.DirectBoat), Times.Once); + _mockRateEvaluationService.Verify(x => x.GetByRateSheetIdsAsync(rateSheetIds), Times.Once); + _mockEvaluationDetailService.Verify(x => x.GetActiveByEvaluationTypeAndIdsAsync(rateRequest.ApplicationDate, EvaluationTypes.Rule, new List()), Times.Once); + } + + [Fact] + public async Task FindRate_NoRatesReturned_ReturnDefault() + { + var rateRequest = new RateRequest { ApplicationDate = DateTime.Now}; + var rateSheetIds = new List { 1, 2, 3 }; + + var rateEvaluations = new List { + new RateEvaluation + { + EvaluationDetailId = 1 + }, + new RateEvaluation + { + EvaluationDetailId = 2 + } + }; + var ratesEvaluationDetailIds = rateEvaluations.GroupBy(rateEvaluation => rateEvaluation.EvaluationDetailId) + .Select(group => group.First().EvaluationDetailId) + .ToList(); + + _mockRateSheetService.Setup(x => x.GetRateSheetIdsByRateTypeAsync(RateTypes.DirectBoat)).ReturnsAsync(rateSheetIds); + _mockRateEvaluationService.Setup(x => x.GetByRateSheetIdsAsync(rateSheetIds)).ReturnsAsync(rateEvaluations); + + _mockEvaluationDetailService.Setup(x => x.GetActiveByEvaluationTypeAndIdsAsync(rateRequest.ApplicationDate, EvaluationTypes.Rule, ratesEvaluationDetailIds)).ReturnsAsync(GetFakeEvaluations()).Verifiable(); + + var evaluationsReturned = new List() + { + new EvaluationDetail + { + EvaluationDetailId = 1 + }, + new EvaluationDetail + { + EvaluationDetailId = 2 + } + }; + _mockRunEvaluationService.Setup(x => x.ValidateRequestRules(rateRequest, It.IsAny>())).Returns(evaluationsReturned).Verifiable(); + + var evaluationIds = evaluationsReturned.Select(evaluationReturned => evaluationReturned.EvaluationDetailId).ToList(); + _mockRateService.Setup(x => x.GetByDateEvaluationIdAndEvaluationTypeAsync(It.IsAny(), evaluationIds, EvaluationTypes.Rule)).ReturnsAsync(new List()).Verifiable(); + + var possibleCalculationIds = new List { 1, 2 }; + _mockEvaluationDetailService.Setup(x => x.GetActiveByEvaluationTypeAndIdsAsync(rateRequest.ApplicationDate, EvaluationTypes.Calculation, possibleCalculationIds)).ReturnsAsync(GetFakeCalculationsEvaluations()).Verifiable(); + + + _mockRunCalculationService.Setup(x => x.CalculateEvaluationWithSetOfRequests(It.IsAny>(), It.IsAny())) + .Returns(new List()); + var result = await _rateFinderService.FindRate(rateRequest); + Assert.Equal(99, result.Rate); + + _mockRateSheetService.Verify(x => x.GetRateSheetIdsByRateTypeAsync(RateTypes.DirectBoat), Times.Once); + _mockRateEvaluationService.Verify(x => x.GetByRateSheetIdsAsync(rateSheetIds), Times.Once); + _mockEvaluationDetailService.Verify(x => x.GetActiveByEvaluationTypeAndIdsAsync(rateRequest.ApplicationDate, EvaluationTypes.Rule, ratesEvaluationDetailIds), Times.Once); ; + _mockRunEvaluationService.Verify(x => x.ValidateRequestRules(It.IsAny(), It.IsAny>()), Times.Once); + _mockRateService.Verify(x => x.GetByDateEvaluationIdAndEvaluationTypeAsync(It.IsAny(), It.IsAny>(), It.IsAny()), Times.Once); + _mockRunCalculationService.Verify(x => x.CalculateEvaluationWithSetOfRequests(It.IsAny>(), It.IsAny()), Times.Never); + } + + [Fact] + public void FindRate_NoRatesReturned_Exception() + { + var rateRequest = new RateRequest { ApplicationDate = DateTime.Now}; + var rateSheetIds = new List { 1, 2, 3 }; + var rateEvaluations = new List { + new RateEvaluation + { + EvaluationDetailId = 600 + }, + new RateEvaluation + { + EvaluationDetailId = 700 + } + }; + var ratesEvaluationDetailIds = rateEvaluations.GroupBy(rateEvaluation => rateEvaluation.EvaluationDetailId) + .Select(group => group.First().EvaluationDetailId) + .ToList(); + + _mockRateSheetService.Setup(x => x.GetRateSheetIdsByRateTypeAsync(RateTypes.DirectBoat)).ReturnsAsync(rateSheetIds); + _mockRateEvaluationService.Setup(x => x.GetByRateSheetIdsAsync(rateSheetIds)).ReturnsAsync(rateEvaluations); + _mockEvaluationDetailService.Setup(x => x.GetActiveByEvaluationTypeAndIdsAsync(rateRequest.ApplicationDate ,EvaluationTypes.Rule, ratesEvaluationDetailIds)).ReturnsAsync(GetFakeEvaluations()).Verifiable(); + + var evaluationsReturned = new List + { + new EvaluationDetail + { + EvaluationDetailId = 600 + }, + new EvaluationDetail + { + EvaluationDetailId = 700 + } + }; + _mockRunEvaluationService.Setup(x => x.ValidateRequestRules(It.IsAny(), It.IsAny>())).Returns(evaluationsReturned).Verifiable(); + + var evaluationIds = new List { 600, 700}; + var date = Convert.ToDateTime("1/1/2023"); + _mockRateService.Setup(x => x.GetByDateEvaluationIdAndEvaluationTypeAsync(date, evaluationIds, EvaluationTypes.Rule)).ReturnsAsync(GetFakeRates()).Verifiable(); + + Assert.ThrowsAsync(async () => await _rateFinderService.FindRate(rateRequest)); + + _mockRateSheetService.Verify(x => x.GetRateSheetIdsByRateTypeAsync(RateTypes.DirectBoat), Times.Once); + _mockRateEvaluationService.Verify(x => x.GetByRateSheetIdsAsync(rateSheetIds), Times.Once); + _mockEvaluationDetailService.Verify(x => x.GetActiveByEvaluationTypeAndIdsAsync(rateRequest.ApplicationDate, EvaluationTypes.Rule, ratesEvaluationDetailIds), Times.Once); + _mockRunEvaluationService.Verify(x => x.ValidateRequestRules(It.IsAny(), It.IsAny>()), Times.Once); + _mockRateService.Verify(x => x.GetByDateEvaluationIdAndEvaluationTypeAsync(It.IsAny(), It.IsAny>(), It.IsAny()), Times.Once); + _mockRunCalculationService.Verify(x => x.CalculateEvaluationWithSetOfRequests(It.IsAny>(), It.IsAny()), Times.Never); + } + + [Fact] + public async Task FindRate_RatesReturned_Success() + { + var rateRequest = new RateRequest { ApplicationDate = DateTime.Now }; + var rateSheetIds = new List { 1, 2, 3 }; + + var rateEvaluations = new List { + new RateEvaluation + { + EvaluationDetailId = 1 + }, + new RateEvaluation + { + EvaluationDetailId = 2 + } + }; + var ratesEvaluationDetailIds = rateEvaluations.GroupBy(rateEvaluation => rateEvaluation.EvaluationDetailId) + .Select(group => group.First().EvaluationDetailId) + .ToList(); + + _mockRateSheetService.Setup(x => x.GetRateSheetIdsByRateTypeAsync(RateTypes.DirectBoat)).ReturnsAsync(rateSheetIds); + _mockRateEvaluationService.Setup(x => x.GetByRateSheetIdsAsync(rateSheetIds)).ReturnsAsync(rateEvaluations); + + _mockEvaluationDetailService.Setup(x => x.GetActiveByEvaluationTypeAndIdsAsync(rateRequest.ApplicationDate, EvaluationTypes.Rule, ratesEvaluationDetailIds)).ReturnsAsync(GetFakeEvaluations()).Verifiable(); + + var evaluationsReturned = new List() + { + new EvaluationDetail + { + EvaluationDetailId = 1 + }, + new EvaluationDetail + { + EvaluationDetailId = 2 + } + }; + _mockRunEvaluationService.Setup(x => x.ValidateRequestRules(rateRequest, It.IsAny>())).Returns(evaluationsReturned).Verifiable(); + + var evaluationIds = evaluationsReturned.Select(evaluationReturned => evaluationReturned.EvaluationDetailId).ToList(); + var rate = GetFakeRates().Where(rate => rate.RateId == 1); + _mockRateService.Setup(x => x.GetByDateEvaluationIdAndEvaluationTypeAsync(It.IsAny(), evaluationIds, EvaluationTypes.Rule)).ReturnsAsync(rate).Verifiable(); + + var possibleCalculationIds = new List { 1, 2 }; + _mockEvaluationDetailService.Setup(x => x.GetActiveByEvaluationTypeAndIdsAsync(rateRequest.ApplicationDate, EvaluationTypes.Calculation, possibleCalculationIds)).ReturnsAsync(GetFakeCalculationsEvaluations()).Verifiable(); + + + _mockRunCalculationService.Setup(x => x.CalculateEvaluationWithSetOfRequests(It.IsAny>(), It.IsAny())) + .Returns(new List()); + var result = await _rateFinderService.FindRate(rateRequest); + Assert.Equal(5.49M, result.Rate); + + _mockRateSheetService.Verify(x => x.GetRateSheetIdsByRateTypeAsync(RateTypes.DirectBoat), Times.Once); + _mockRateEvaluationService.Verify(x => x.GetByRateSheetIdsAsync(rateSheetIds), Times.Once); + _mockEvaluationDetailService.Verify(x => x.GetActiveByEvaluationTypeAndIdsAsync(rateRequest.ApplicationDate, EvaluationTypes.Rule, ratesEvaluationDetailIds), Times.Once); ; + _mockRunEvaluationService.Verify(x => x.ValidateRequestRules(It.IsAny(), It.IsAny>()), Times.Once); + _mockRateService.Verify(x => x.GetByDateEvaluationIdAndEvaluationTypeAsync(It.IsAny(), It.IsAny>(), It.IsAny()), Times.Once); + _mockRunCalculationService.Verify(x => x.CalculateEvaluationWithSetOfRequests(It.IsAny>(), It.IsAny()), Times.Exactly(2)); + } + + public static List GetFakeEvaluations() + { + var evaluationBuilder1 = new EvaluationDetail() + { + EvaluationDetailId = 1, + Description = "CreditScore Greater Than Or Equal 740.", + EvaluationParameters = new List() + { + new EvaluationParameter(0, ParameterTypes.Variable, "RateRequest.CreditScore"), + new EvaluationParameter(1, ParameterTypes.GreaterThanOrEqual, ""), + new EvaluationParameter(2, ParameterTypes.TextInput, "740") + } + }; + + var evaluationBuilder2 = new EvaluationDetail() + { + EvaluationDetailId = 2, + Description = "Model Year Greater than or Equal 2022.", + EvaluationParameters = new List() + { + new EvaluationParameter(0, ParameterTypes.Variable, "RateRequest.ModelYear"), + new EvaluationParameter(1, ParameterTypes.GreaterThanOrEqual, ""), + new EvaluationParameter(2, ParameterTypes.TextInput, "2022") + } + }; + + var evaluationBuilder3 = new EvaluationDetail() + { + EvaluationDetailId = 3, + Description = "Model Year Equal 2021.", + EvaluationParameters = new List() + { + new EvaluationParameter(0, ParameterTypes.Variable, "RateRequest.ModelYear"), + new EvaluationParameter(1, ParameterTypes.Equal, ""), + new EvaluationParameter(2, ParameterTypes.TextInput, "2021") + } + }; + + var evaluationBuilder4 = new EvaluationDetail() + { + EvaluationDetailId = 4, + Description = "CreditScore Greater Than Or Equal 700 AND Less Than Or Equal 739.", + EvaluationParameters = new List() + { + new EvaluationParameter(0, ParameterTypes.ParenthesesStart, ""), + new EvaluationParameter(1, ParameterTypes.Variable, "RateRequest.CreditScore"), + new EvaluationParameter(2, ParameterTypes.GreaterThanOrEqual, ""), + new EvaluationParameter(3, ParameterTypes.TextInput, "700"), + new EvaluationParameter(4, ParameterTypes.ParenthesesEnd, ""), + new EvaluationParameter(5, ParameterTypes.And, ""), + new EvaluationParameter(6, ParameterTypes.ParenthesesStart, ""), + new EvaluationParameter(7, ParameterTypes.Variable, "RateRequest.CreditScore"), + new EvaluationParameter(8, ParameterTypes.LessThanOrEqual, ""), + new EvaluationParameter(9, ParameterTypes.TextInput, "739"), + new EvaluationParameter(10, ParameterTypes.ParenthesesEnd, "") + } + }; + + var evaluations = new List() { evaluationBuilder1, evaluationBuilder2, evaluationBuilder3, evaluationBuilder4 }; + return evaluations; + } + + public static List GetFakeCalculationsEvaluations() + { + var evaluationBuilder1 = new EvaluationDetail() + { + EvaluationDetailId = 5, + Description = "CreditScore - 1.", + EvaluationReturnType = EvaluationReturnTypes.Decimal, + EvaluationType = EvaluationTypes.Calculation, + EvaluationParameters = new List() + { + new EvaluationParameter(0, ParameterTypes.Variable, "RateRequest.CreditScore"), + new EvaluationParameter(1, ParameterTypes.Subtraction, ""), + new EvaluationParameter(2, ParameterTypes.TextInput, "1") + } + }; + + var evaluationBuilder2 = new EvaluationDetail() + { + EvaluationDetailId = 6, + Description = "CreditScore - 2.", + EvaluationReturnType = EvaluationReturnTypes.Decimal, + EvaluationType = EvaluationTypes.Calculation, + EvaluationParameters = new List() + { + new EvaluationParameter(0, ParameterTypes.Variable, "RateRequest.CreditScore"), + new EvaluationParameter(1, ParameterTypes.Subtraction, ""), + new EvaluationParameter(2, ParameterTypes.TextInput, "2") + } + }; + + var evaluations = new List() { evaluationBuilder1, evaluationBuilder2 }; + return evaluations; + } + + public static List GetFakeRates() + { + var rate1 = new Rate() + { + RateId = 1, + Value = 5.49M, + RateEvaluations = new List() + { + new RateEvaluation() + { + RateEvaluationId = 1, + RateId = 1, + EvaluationDetailId = 1, + }, + new RateEvaluation() + { + RateEvaluationId = 2, + RateId = 1, + EvaluationDetailId = 2, + } + } + }; + + var rate2 = new Rate() + { + RateId = 2, + Value = 5.50M, + RateEvaluations = new List() + { + new RateEvaluation() + { + RateEvaluationId = 3, + RateId = 2, + EvaluationDetailId = 1, + }, + new RateEvaluation() + { + RateEvaluationId = 4, + RateId = 2, + EvaluationDetailId = 3, + } + } + }; + + var rate3 = new Rate() + { + RateId = 3, + Value = 5.51M, + RateEvaluations = new List() + { + new RateEvaluation() + { + RateEvaluationId = 5, + RateId = 3, + EvaluationDetailId = 4, + } + } + }; + + var rates = new List() { rate1, rate2, rate3 }; + return rates; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoanRateOperationServicesTests/RateUploaderServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoanRateOperationServicesTests/RateUploaderServiceTest.cs new file mode 100644 index 0000000..3789f19 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoanRateOperationServicesTests/RateUploaderServiceTest.cs @@ -0,0 +1,781 @@ +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.Library.Internal.LcuEvaluation.Services; +using LCU.Library.Internal.Exceptions; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Requests; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +using LCU.API.Internal.LoanRate.Services; +using LCU.API.Internal.LoanRate.Services.MicroApiServices; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Responses; +using LCU.Library.Internal.LcuEvaluationModels.Entities; +using LCU.Library.Internal.LoanRateModels.Enums; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.TypeExtensions.EnumExtensions; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests.LoanRateOperationServicesTests; + +public class RateUploaderServiceTest +{ + private readonly RateUploaderService _rateUploaderService; + private readonly Mock _mockRateService; + private readonly Mock _mockRateSheetService; + private readonly Mock _mockEvaluationDetailService; + private readonly Mock _mockRateEvaluationService; + private readonly Mock _mockRateUploadInfoService; + private readonly Mock _mockFileOperationsApiService; + + public RateUploaderServiceTest() + { + _mockRateService = new Mock(); + _mockRateSheetService = new Mock(); + _mockFileOperationsApiService = new Mock(); + _mockEvaluationDetailService = new Mock(); + _mockRateEvaluationService = new Mock(); + _mockRateUploadInfoService = new Mock(); + _rateUploaderService = new RateUploaderService(_mockRateService.Object, + _mockRateSheetService.Object, + _mockEvaluationDetailService.Object, + _mockRateEvaluationService.Object, + _mockRateUploadInfoService.Object, + _mockFileOperationsApiService.Object); + } + + public ExcelDataResponse FakeExcelDataResponse() + { + return new ExcelDataResponse + { + WorkBook = new WorkBook + { + SheetResponses = new List + { + new SheetResponse(RateTypes.DirectAuto.GetNameFromValue()) + { + CellResponses = new List + { + new CellResponse + { + ColumnName = "A", + Row = 10, + Value = "2.2", + Formula = "" + }, + new CellResponse + { + ColumnName = "B", + Row = 11, + Value = "2.3", + Formula = "" + }, + new CellResponse + { + ColumnName = "C", + Row = 12, + Value = "2.4", + Formula = "" + }, + new CellResponse + { + ColumnName = "D", + Row = 13, + Value = "2.5", + Formula = "" + } + } + } + } + } + }; + } + + public List FakeRates() + { + return new List + { + new Rate + { + RateId = 1, + Column = "A", + Row = "10", + RateSheetId = 1, + Value = 2.1M, + RateStartDate = Convert.ToDateTime("1-1-2023"), + RateEvaluations = new List + { + new RateEvaluation + { + RateEvaluationId = 1, + RateId = 1, + EvaluationDetailId = 1 + } + } + }, + new Rate + { + RateId = 2, + Column = "B", + Row = "11", + RateSheetId = 1, + Value = 0.0M, + RateStartDate = Convert.ToDateTime("1-1-2023"), + RateEvaluations = new List + { + new RateEvaluation + { + RateEvaluationId = 2, + RateId = 2, + EvaluationDetailId = 2 + } + } + } + }; + } + + public List FakeRatesToUpdate() + { + return new List() + { + new Rate + { + RateId = 1, + Column = "A", + Row = "10", + RateSheetId = 1, + Value = 2.2M, + RateStartDate = Convert.ToDateTime("1-1-2023"), + RateEvaluations = new List + { + new RateEvaluation + { + RateEvaluationId = 1, + RateId = 1, + EvaluationDetailId = 1 + } + } + }, + new Rate + { + RateId = 2, + Column = "B", + Row = "11", + RateSheetId = 1, + Value = 2.3M, + RateStartDate = Convert.ToDateTime("1-1-2023"), + RateEvaluations = new List + { + new RateEvaluation + { + RateEvaluationId = 2, + RateId = 2, + EvaluationDetailId = 2 + } + } + } + }; + } + + public List FakeRatesToCreate() + { + return new List + { + new Rate + { + RateId = 0, + Column = "C", + Row = "12", + RateSheetId = 1, + Value = 2.4M, + RateStartDate = Convert.ToDateTime("1-1-2023") + }, + new Rate + { + RateId = 0, + Column = "D", + Row = "13", + RateSheetId = 1, + Value = 2.5M, + RateStartDate = Convert.ToDateTime("1-1-2023") + } + }; + } + + private List FakeRateSheets() + { + var rateTypes = Enum.GetValues(typeof(RateTypes)).Cast().ToList(); + return rateTypes.Select((t, i) => new RateSheet { RateSheetId = i + 1, RateType = t }).ToList(); + } + + private RateUploadInfo FakeRateUploadInfo() + => new () + { + EffectiveDate = DateTime.Now + }; + + [Fact] + public async Task RateUploader_UploadRates_RateExcelPath_Success() + { + var effectiveDate = DateTime.Now; + var request = new RateUploadRequest + { + FileName = "Test", + FileContent = "Test", + EffectiveDate = effectiveDate + }; + + var excelDataResponse = new ExcelDataResponse + { + WorkBook = new WorkBook + { + SheetResponses = new List + { + new SheetResponse(RateTypes.DirectBoat.GetNameFromValue()) + { + CellResponses = new List + { + new CellResponse + { + ColumnName = "A", + Row = 10, + Value = "2.2", + Formula = "" + }, + new CellResponse + { + ColumnName = "B", + Row = 11, + Value = "2.3", + Formula = "" + } + } + } + } + } + }; + + var rates = FakeRates(); + var rateSheets = FakeRateSheets(); + var ratesToCreate = FakeRatesToCreate(); + var previousRateUploadInfo = FakeRateUploadInfo(); + + _mockRateService.Setup(x => x.GetByDateAsync(It.IsAny())).ReturnsAsync(rates); + _mockRateSheetService.Setup(x => x.GetAsync()).ReturnsAsync(rateSheets); + _mockFileOperationsApiService.Setup(x => x.DirectExtractExcelWorkBookData(It.IsAny())).ReturnsAsync(excelDataResponse).Verifiable(); + + _mockRateUploadInfoService.Setup(x => x.SaveAsync( It.Is(rateUploadInfo => rateUploadInfo.Rates.Count == ratesToCreate.Count))); + _mockRateUploadInfoService.Setup(x => x.GetLatestVersionOnDate(effectiveDate)).ReturnsAsync(previousRateUploadInfo).Verifiable(); + + await _rateUploaderService.UploadRates(request); + + _mockRateService.Verify(x => x.GetByDateAsync(It.IsAny()), Times.Once); + _mockFileOperationsApiService.Verify(x => x.DirectExtractExcelWorkBookData(It.IsAny()), Times.Once); + _mockRateUploadInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Once); + } + + [Fact] + public void RateUploader_UploadRates_RateExcelPath_RateServiceGetByDate_ThrowsException() + { + var request = new RateUploadRequest + { + FileContent = "Test", + FileName = "Test", + EffectiveDate = DateTime.Now + }; + + var excelDataResponse = FakeExcelDataResponse(); + var ratesToUpdate = FakeRatesToUpdate(); + var ratesToCreate = FakeRatesToCreate(); + + _mockRateService.Setup(x => x.GetByDateAsync(It.IsAny())).Throws(new Exception("Test")).Verifiable(); + _mockFileOperationsApiService.Setup(x => x.DirectExtractExcelWorkBookData(It.IsAny())).ReturnsAsync(excelDataResponse).Verifiable(); + _mockRateUploadInfoService.Setup(x => x.SaveAsync( It.Is(rateUploadInfo => rateUploadInfo.Rates.Count == ratesToCreate.Count))); + _mockRateService.Setup(x => x.UpdateRangeAsync(ratesToUpdate)).Verifiable(); + + Assert.ThrowsAsync(async () => await _rateUploaderService.UploadRates(request)); + _mockRateService.Verify(x => x.GetByDateAsync(It.IsAny()), Times.Once); + _mockFileOperationsApiService.Verify(x => x.DirectExtractExcelWorkBookData(It.IsAny()), Times.Never); + _mockRateUploadInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Never); + _mockRateService.Verify(x => x.UpdateRangeAsync(It.Is>(rates => rates.Count() == 2)), Times.Never); + } + + [Fact] + public void RateUploader_UploadRates_RateExcelPath_FileOperations_ThrowsException() + { + var request = new RateUploadRequest + { + FileName = "Test", + FileContent = "Test", + EffectiveDate = DateTime.Now + }; + + var rates = FakeRates(); + var rateSheets = FakeRateSheets(); + var ratesToUpdate = FakeRatesToUpdate(); + var ratesToCreate = FakeRatesToCreate(); + + _mockRateService.Setup(x => x.GetByDateAsync(It.IsAny())).ReturnsAsync(rates); + _mockRateSheetService.Setup(x => x.GetAsync()).ReturnsAsync(rateSheets); + _mockFileOperationsApiService.Setup(x => x.DirectExtractExcelWorkBookData(It.IsAny())).Throws(new Exception("Test")).Verifiable(); + _mockRateUploadInfoService.Setup(x => x.SaveAsync( It.Is(rateUploadInfo => rateUploadInfo.Rates.Count == ratesToCreate.Count))); + _mockRateService.Setup(x => x.UpdateRangeAsync(ratesToUpdate)).Verifiable(); + + Assert.ThrowsAsync(async () => await _rateUploaderService.UploadRates(request)); + + _mockRateService.Verify(x => x.GetByDateAsync(It.IsAny()), Times.Once); + _mockFileOperationsApiService.Verify(x => x.DirectExtractExcelWorkBookData(It.IsAny()), Times.Once); + + _mockRateUploadInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Never); + _mockRateService.Verify(x => x.UpdateRangeAsync(It.Is>(rates => rates.Count() == 2)), Times.Never); + } + + [Fact] + public async Task RateUploader_UploadRates_ExcelDataRequest_Success() + { + var effectiveDate = DateTime.Now; + var fileContent = "Test"; + var request = new RateUploadExcelDataRequest + { + EffectiveDate = effectiveDate, + ExcelDataRequest = new ExcelDataRequest + { + FileContent = fileContent, + SheetRequests = new List + { + new() + { + SheetName = RateTypes.DirectBoat.GetNameFromValue(), + Rows = new List { 10, 11, 12, 13 }, + Columns = new List { "A", "B", "C", "D" } + } + } + } + }; + + var excelDataResponse = FakeExcelDataResponse(); + var rates = FakeRates(); + var rateSheets = FakeRateSheets(); + var previousRateUploadInfo = FakeRateUploadInfo(); + + _mockFileOperationsApiService.Setup(x => x.DirectExtractExcelWorkBookData(request.ExcelDataRequest)).ReturnsAsync(excelDataResponse).Verifiable(); + _mockRateSheetService.Setup(x => x.GetAsync()).ReturnsAsync(rateSheets); + _mockRateService.Setup(x => x.GetByDateAsync(It.IsAny())).ReturnsAsync(rates); + _mockRateUploadInfoService.Setup(x => x.SaveAsync(It.IsAny())).Verifiable(); + _mockRateUploadInfoService.Setup(x => x.GetLatestVersionOnDate(effectiveDate)).ReturnsAsync(previousRateUploadInfo).Verifiable(); + + await _rateUploaderService.UploadRates(request); + + _mockFileOperationsApiService.Verify(x => x.DirectExtractExcelWorkBookData(It.IsAny()), Times.Once); + _mockRateService.Verify(x => x.GetByDateAsync(It.IsAny()), Times.Once); + _mockRateUploadInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Once); + } + + [Fact] + public void RateUploader_UploadRates_ExcelDataRequest_FileOperations_ThrowsException() + { + var fileContent = "Test"; + var request = new RateUploadExcelDataRequest + { + EffectiveDate = DateTime.Now, + ExcelDataRequest = new ExcelDataRequest + { + FileContent = fileContent, + SheetRequests = new List + { + new() + { + SheetName = "Test", + Rows = new List { 10, 11, 12, 13 }, + Columns = new List { "A", "B", "C", "D" } + } + } + } + }; + + var rates = FakeRates(); + var ratesToCreate = FakeRatesToCreate(); + + _mockFileOperationsApiService.Setup(x => x.DirectExtractExcelWorkBookData(request.ExcelDataRequest)).Throws(new Exception("Test")).Verifiable(); + _mockRateService.Setup(x => x.GetByDateAsync(It.IsAny())).ReturnsAsync(rates); + _mockRateUploadInfoService.Setup(x => x.SaveAsync( It.Is(rateUploadInfo => rateUploadInfo.Rates.Count == ratesToCreate.Count))); + + Assert.ThrowsAsync(async () => await _rateUploaderService.UploadRates(request)); + + _mockFileOperationsApiService.Verify(x => x.DirectExtractExcelWorkBookData(It.IsAny()), Times.Once); + _mockRateService.Verify(x => x.GetByDateAsync(It.IsAny()), Times.Never); + _mockRateUploadInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Never); + } + + [Fact] + public void RateUploader_UploadRates_ExcelDataRequest_RateServiceGetByDate_ThrowsException() + { + var fileContent = "Test"; + var request = new RateUploadExcelDataRequest + { + EffectiveDate = DateTime.Now, + ExcelDataRequest = new ExcelDataRequest + { + FileContent = fileContent, + SheetRequests = new List + { + new() + { + SheetName = "Test", + Rows = new List { 10, 11, 12, 13 }, + Columns = new List { "A", "B", "C", "D" } + } + } + } + }; + + var excelDataResponse = FakeExcelDataResponse(); + var ratesToCreate = FakeRatesToCreate(); + + _mockFileOperationsApiService.Setup(x => x.DirectExtractExcelWorkBookData(request.ExcelDataRequest)).ReturnsAsync(excelDataResponse).Verifiable(); + _mockRateService.Setup(x => x.GetByDateAsync(It.IsAny())).Throws(new Exception("Test")).Verifiable(); + _mockRateUploadInfoService.Setup(x => x.SaveAsync( It.Is(rateUploadInfo => rateUploadInfo.Rates.Count == ratesToCreate.Count))); + + Assert.ThrowsAsync(async () => await _rateUploaderService.UploadRates(request)); + + _mockFileOperationsApiService.Verify(x => x.DirectExtractExcelWorkBookData(It.IsAny()), Times.Once); + _mockRateService.Verify(x => x.GetByDateAsync(It.IsAny()), Times.Once); + _mockRateUploadInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Never); + } + + [Fact] + public void RateUploader_BuildExcelDataRequest_Succcess() + { + var rateExcelContent = "Test"; + var rates = FakeRates(); + var rateSheets = FakeRateSheets(); + var excelDataRequest = _rateUploaderService.BuildExcelDataRequest(rateExcelContent, rates, rateSheets); + + Assert.NotNull(excelDataRequest); + Assert.NotNull(excelDataRequest.SheetRequests); + Assert.Single(excelDataRequest.SheetRequests); + var sheetRequest = excelDataRequest.SheetRequests.FirstOrDefault(); + Assert.NotNull(sheetRequest); + Assert.NotNull(sheetRequest.Rows); + Assert.Equal(2, sheetRequest.Rows.Count); + Assert.NotNull(sheetRequest.Columns); + Assert.Equal(2, sheetRequest.Columns.Count); + Assert.Equal(rateExcelContent, excelDataRequest.FileContent); + } + + [Fact] + public async Task RateUploader_WorkThroughExcelDataResponseAndUpdateRates_Success() + { + var excelDataResponse = FakeExcelDataResponse(); + var rates = FakeRates(); + var rateSheets = FakeRateSheets(); + var ratesToCreate = FakeRatesToCreate(); + var rateUploadInfo = FakeRateUploadInfo(); + rateUploadInfo.Rates = ratesToCreate; + + _mockRateUploadInfoService.Setup(x => x.SaveAsync(It.IsAny())).Verifiable(); + + await _rateUploaderService.WorkThroughExcelDataResponseAndUpdateRates(excelDataResponse, rateUploadInfo, rates, rateSheets, DateTime.Now); + + _mockRateUploadInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task RateUploader_WorkThroughExcelDataResponseAndUpdateRates_NullWorkbook_Done() + { + var excelDataResponse = new ExcelDataResponse + { + WorkBook = null + }; + var rates = FakeRates(); + var rateSheets = FakeRateSheets(); + var rateUploadInfo = FakeRateUploadInfo(); + + await _rateUploaderService.WorkThroughExcelDataResponseAndUpdateRates(excelDataResponse, rateUploadInfo, rates, rateSheets, DateTime.Now); + + _mockRateUploadInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Never); + _mockRateService.Verify(x => x.UpdateRangeAsync(It.IsAny>()), Times.Never); + } + + [Fact] + public async Task RateUploader_WorkThroughExcelDataResponseAndUpdateRates_NullSheetResponses_Done() + { + var excelDataResponse = new ExcelDataResponse + { + WorkBook = new WorkBook + { + SheetResponses = null + } + }; + var rates = FakeRates(); + var rateSheets = FakeRateSheets(); + var rateUploadInfo = FakeRateUploadInfo(); + + await _rateUploaderService.WorkThroughExcelDataResponseAndUpdateRates(excelDataResponse, rateUploadInfo, rates, rateSheets, DateTime.Now); + + _mockRateUploadInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Never); + _mockRateService.Verify(x => x.UpdateRangeAsync(It.IsAny>()), Times.Never); + } + + [Fact] + public void RateUploader_WorkThroughExcelDataResponseAndUpdateRates_NonDecimalCellValue_ServiceException() + { + var excelDataResponse = new ExcelDataResponse + { + WorkBook = new WorkBook + { + SheetResponses = new List + { + new SheetResponse("Test") + { + CellResponses = new List + { + new CellResponse + { + ColumnName = "A", + Row = 10, + Value = "2.2", + Formula = "" + }, + new CellResponse + { + ColumnName = "B", + Row = 11, + Value = "2.3", + Formula = "" + }, + new CellResponse + { + ColumnName = "C", + Row = 12, + Value = "TEST", + Formula = "" + }, + new CellResponse + { + ColumnName = "D", + Row = 13, + Value = "2.5", + Formula = "" + } + } + } + } + } + }; + var rates = FakeRates(); + var rateSheets = FakeRateSheets(); + var rateUploadInfo = FakeRateUploadInfo(); + + Assert.ThrowsAsync( + async () => await _rateUploaderService.WorkThroughExcelDataResponseAndUpdateRates(excelDataResponse, rateUploadInfo, rates, rateSheets, DateTime.Now)); + _mockRateUploadInfoService.Verify(x => x.SaveAsync(It.IsAny()), Times.Never); + _mockRateService.Verify(x => x.UpdateRangeAsync(It.IsAny>()), Times.Never); + } + + [Fact] + public async Task RateUploader_InitiateRatesEvaluations_Success() + { + var rateSheets = FakeRateSheets(); + var requests = new List + { + new() + { + SheetName = rateSheets[0].SheetName, + EvaluationDetailId = DateTime.Now.Millisecond, + RowsOrColumns = new List { Guid.NewGuid().ToString() } + }, + new() + { + SheetName = rateSheets[1].SheetName, + EvaluationDetailId = DateTime.Now.Millisecond, + RowsOrColumns = new List { Guid.NewGuid().ToString() } + }, + new() + { + SheetName = rateSheets[2].SheetName, + EvaluationDetailId = DateTime.Now.Millisecond, + RowsOrColumns = new List { Guid.NewGuid().ToString() } + } + }; + + var rules = new List + { + new() { EvaluationDetailId = requests[0].EvaluationDetailId }, + new() { EvaluationDetailId = requests[1].EvaluationDetailId }, + new() { EvaluationDetailId = requests[2].EvaluationDetailId } + }; + + var rates = new List + { + new() + { + RateSheetId = rateSheets[0].RateSheetId, + Column = requests[0].RowsOrColumns[0], + RateEvaluations = new List() + }, + new() + { + RateSheetId = rateSheets[0].RateSheetId, + Column = requests[0].RowsOrColumns[0], + RateEvaluations = new List() + }, + new() + { + RateSheetId = rateSheets[0].RateSheetId, + Row = requests[0].RowsOrColumns[0], + RateEvaluations = new List() + }, + new() + { + RateSheetId = rateSheets[1].RateSheetId, + Column = requests[1].RowsOrColumns[0], + RateEvaluations = new List() + }, + new() + { + RateSheetId = rateSheets[1].RateSheetId, + Column = requests[1].RowsOrColumns[0], + RateEvaluations = new List() + }, + new() + { + RateSheetId = rateSheets[2].RateSheetId, + Column = requests[2].RowsOrColumns[0], + RateEvaluations = new List() + } + }; + + var tab1 = rates.Where(rate => rate.RateSheetId == rateSheets[0].RateSheetId); + var tab2 = rates.Where(rate => rate.RateSheetId == rateSheets[1].RateSheetId); + var tab3 = rates.Where(rate => rate.RateSheetId == rateSheets[2].RateSheetId); + + _mockRateSheetService.SetupSequence(x => x.GetRatesByRateTypeAsync(It.IsAny())).ReturnsAsync(tab1) + .ReturnsAsync(tab2) + .ReturnsAsync(tab3); + _mockEvaluationDetailService.SetupSequence(x => x.GetByIdAsync(It.IsAny())).ReturnsAsync(rules[0]) + .ReturnsAsync(rules[1]) + .ReturnsAsync(rules[2]); + + var result = (await _rateUploaderService.InitiateRatesEvaluations(requests)).ToList(); + Assert.NotNull(result); + Assert.NotEmpty(result); + Assert.Equal(3, result.Count(x => x.Success)); + + _mockRateSheetService.Verify(x => x.GetRatesByRateTypeAsync(It.IsAny()), Times.Exactly(3)); + _mockEvaluationDetailService.Verify(x => x.GetByIdAsync(It.IsAny()), Times.Exactly(3)); + _mockRateEvaluationService.Verify(x => x.SaveRangeAsync(It.Is>( savedRates => savedRates.Count == 5 )), Times.Once); + } + + [Fact] + public async Task RateUploader_InitiateRatesEvaluations_NotFound_Success() + { + var rateSheets = FakeRateSheets(); + var requests = new List + { + new() + { + SheetName = rateSheets[0].SheetName, + EvaluationDetailId = DateTime.Now.Millisecond, + RowsOrColumns = new List { Guid.NewGuid().ToString() } + }, + new() + { + SheetName = rateSheets[1].SheetName, + EvaluationDetailId = DateTime.Now.Millisecond, + RowsOrColumns = new List { Guid.NewGuid().ToString() } + }, + new() + { + SheetName = rateSheets[2].SheetName, + EvaluationDetailId = DateTime.Now.Millisecond, + RowsOrColumns = new List { Guid.NewGuid().ToString() } + } + }; + + var rules = new List + { + new() { EvaluationDetailId = requests[0].EvaluationDetailId }, + null, + new() { EvaluationDetailId = requests[2].EvaluationDetailId } + }; + + var rates = new List + { + new() + { + RateSheetId = rateSheets[0].RateSheetId, + Column = requests[0].RowsOrColumns[0], + RateEvaluations = new List() + }, + new() + { + RateSheetId = rateSheets[0].RateSheetId, + Column = requests[0].RowsOrColumns[0], + RateEvaluations = new List() + }, + new() + { + RateSheetId = rateSheets[0].RateSheetId, + Column = requests[0].RowsOrColumns[0], + RateEvaluations = new List() + }, + new() + { + RateSheetId = rateSheets[1].RateSheetId, + Column = requests[1].RowsOrColumns[0], + RateEvaluations = new List() + }, + new() + { + RateSheetId = rateSheets[1].RateSheetId, + Column = requests[1].RowsOrColumns[0], + RateEvaluations = new List() + }, + new() + { + RateSheetId = rateSheets[2].RateSheetId, + Column = requests[2].RowsOrColumns[0], + RateEvaluations = new List() + } + }; + + var tab1 = rates.Where(rate => rate.RateSheetId == rateSheets[0].RateSheetId); + var tab2 = rates.Where(rate => rate.RateSheetId == rateSheets[1].RateSheetId); + var tab3 = rates.Where(rate => rate.RateSheetId == rateSheets[2].RateSheetId); + + _mockRateSheetService.Setup(x => x.GetAsync()).ReturnsAsync(rateSheets); + _mockRateSheetService.SetupSequence(x => x.GetRatesByRateTypeAsync(It.IsAny())).ReturnsAsync(tab1) + .ReturnsAsync(tab2) + .ReturnsAsync(tab3); + _mockEvaluationDetailService.SetupSequence(x => x.GetByIdAsync(It.IsAny())).ReturnsAsync(rules[0]) + .ReturnsAsync(rules[1]) + .ReturnsAsync(rules[2]); + + var result = (await _rateUploaderService.InitiateRatesEvaluations(requests)).ToList(); + Assert.NotNull(result); + Assert.NotEmpty(result); + Assert.Equal(3, result.Count(x => x.Success)); + + _mockRateSheetService.Verify(x => x.GetRatesByRateTypeAsync(It.IsAny()), Times.Exactly(3)); + _mockEvaluationDetailService.Verify(x => x.GetByIdAsync(It.IsAny()), Times.Exactly(3)); + _mockRateEvaluationService.Verify(x => x.SaveRangeAsync(It.Is>( savedRates => savedRates.Count == 4 )), Times.Once); + } + + [Fact] + public async Task RateUploader_InitiateRatesEvaluations_ThrowsException_Failure() + { + var requests = new List + { + new() + { + SheetName = RateTypes.DirectMotorcycle.GetNameFromValue(), + EvaluationDetailId = DateTime.Now.Millisecond, + RowsOrColumns = new List { Guid.NewGuid().ToString() } + } + }; + + _mockRateSheetService.Setup(x => x.GetRatesByRateTypeAsync(It.IsAny())).Throws(new Exception("Test")).Verifiable(); + + await Assert.ThrowsAsync(async () => await _rateUploaderService.InitiateRatesEvaluations(requests)); + + _mockRateSheetService.Verify(x => x.GetRatesByRateTypeAsync(It.IsAny()), Times.Once); + _mockEvaluationDetailService.Verify(x => x.GetByIdAsync(It.IsAny()), Times.Never); + _mockRateEvaluationService.Verify(x => x.SaveRangeAsync(It.IsAny>()), Times.Never); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoanRateOperationServicesTests/RateValidationServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoanRateOperationServicesTests/RateValidationServiceTest.cs new file mode 100644 index 0000000..58a9b3b --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/LoanRateOperationServicesTests/RateValidationServiceTest.cs @@ -0,0 +1,176 @@ +using LCU.API.Internal.LoanRate.Services; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LcuClassPatterns.CrudTests.Utilities; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.Library.Internal.LoanRateModels.Responses.Validation; +using LCU.Library.Internal.LoanRateModels.Responses.Validation.RateSheetValidations; +using LCU.Library.Internal.TypeExtensions.ObjectExtensions; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests.LoanRateOperationServicesTests; +public class RateValidationServiceTest +{ + private readonly RateValidationService _rateValidationService; + private readonly Mock _mockRateService; + public RateValidationServiceTest() + { + _mockRateService = new Mock(); + _rateValidationService = new RateValidationService(_mockRateService.Object); + } + + [Fact] + public async Task RateValidation_ValidateAsync_Success_AllPass() + { + var rates = FakeRates(); + + foreach (var rate in rates) + { + var originalRate = FakeNewRate(rate, decimal.ToInt32(rate.Value - new Random().Next(0, 10)), decimal.ToInt32(rate.Value)); + var rateHistory = FakeRateHistory(rate, + originalRate, + decimal.ToInt32(rate.Value - new Random().Next(0, 10)), + decimal.ToInt32(rate.Value + new Random().Next(0, 10))); + + _mockRateService.Setup(x => x.GetRateHistory(It.IsAny())).ReturnsAsync(rateHistory); + _mockRateService.Setup(x => x.GetByIdAsync(It.IsAny())).ReturnsAsync(originalRate); + } + + var result = await _rateValidationService.ValidateAsync(rates); + + Assert.NotNull(result); + Assert.True(result.All(x => x.Pass)); + Assert.True(result.All(x => x.Validations.Count > 0)); + + _mockRateService.Verify(x => x.GetRateHistory(It.IsAny()), Times.Exactly(rates.Count)); + _mockRateService.Verify(x => x.GetByIdAsync(It.IsAny()), Times.Exactly(rates.Count)); + } + + [Fact] + public async Task RateValidation_ValidateAsync_Success_DoesNotPassRange() + { + var rates = FakeRates(); + + foreach (var rate in rates) + { + var originalRate = FakeNewRate(rate, decimal.ToInt32(rate.Value - new Random().Next(0, 10)), decimal.ToInt32(rate.Value)); + var rateHistory = FakeRateHistory(rate, + originalRate, + decimal.ToInt32(rate.Value - new Random().Next(0, 10)), + decimal.ToInt32(rate.Value + new Random().Next(0, 10))); + originalRate.Value += 100; + rate.Value += 100; + + _mockRateService.Setup(x => x.GetRateHistory(It.IsAny())).ReturnsAsync(rateHistory); + _mockRateService.Setup(x => x.GetByIdAsync(It.IsAny())).ReturnsAsync(originalRate); + } + + var result = await _rateValidationService.ValidateAsync(rates); + + Assert.NotNull(result); + Assert.False(result.All(x => x.Pass)); + Assert.True(result.All(x => x.Validations.Count > 0)); + Assert.True(result.All(x => x.Validations.First(y => y.RateSheetValidationType == RateSheetValidationTypes.IsWithinTrendRange).Pass)); + Assert.False(result.All(x => x.Validations.First(y => y.RateSheetValidationType == RateSheetValidationTypes.IsWithinRange).Pass)); + + _mockRateService.Verify(x => x.GetRateHistory(It.IsAny()), Times.Exactly(rates.Count)); + _mockRateService.Verify(x => x.GetByIdAsync(It.IsAny()), Times.Exactly(rates.Count)); + } + + [Fact] + public async Task RateValidation_ValidateAsync_Success_DoesNotPassTrend() + { + var rates = FakeRates(); + + foreach (var rate in rates) + { + var originalRate = FakeNewRate(rate, decimal.ToInt32(rate.Value - new Random().Next(0, 10)), decimal.ToInt32(rate.Value)); + var rateHistory = FakeRateHistory(rate, + originalRate, + decimal.ToInt32(rate.Value - new Random().Next(0, 10)), + decimal.ToInt32(rate.Value + new Random().Next(0, 10))); + rateHistory.First().Value += 100; + + _mockRateService.Setup(x => x.GetRateHistory(It.IsAny())).ReturnsAsync(rateHistory); + _mockRateService.Setup(x => x.GetByIdAsync(It.IsAny())).ReturnsAsync(originalRate); + } + + var result = await _rateValidationService.ValidateAsync(rates); + + Assert.NotNull(result); + Assert.False(result.All(x => x.Pass)); + Assert.True(result.All(x => x.Validations.Count > 0)); + Assert.False(result.All(x => x.Validations.First(y => y.RateSheetValidationType == RateSheetValidationTypes.IsWithinTrendRange).Pass)); + Assert.True(result.All(x => x.Validations.First(y => y.RateSheetValidationType == RateSheetValidationTypes.IsWithinRange).Pass)); + + _mockRateService.Verify(x => x.GetRateHistory(It.IsAny()), Times.Exactly(rates.Count)); + _mockRateService.Verify(x => x.GetByIdAsync(It.IsAny()), Times.Exactly(rates.Count)); + } + + [Fact] + public async Task RateValidation_ValidateAsync_ThrowsException() + { + var rates = FakeRates(); + + _mockRateService.Setup(x => x.GetRateHistory(rates[0])).Throws(new Exception("Test")).Verifiable(); + + await Assert.ThrowsAsync(async () => await _rateValidationService.ValidateAsync(rates)); + + _mockRateService.Verify(x => x.GetRateHistory(It.IsAny()), Times.Once); + _mockRateService.Verify(x => x.GetByIdAsync(It.IsAny()), Times.Never); + } + + private List FakeRates(int count = 3) + { + var rates = new List(); + + for (var i = 0; i < count; i++) + { + var startDate = (DateTime)typeof(DateTime).GetRandomValue(); + rates.Add(new Rate + { + RateId = (int)typeof(int).GetRandomValue(), + Value = new Random().Next(20, 30), + Row = (string)typeof(string).GetRandomValue(), + Column = (string)typeof(string).GetRandomValue(), + RateStartDate = startDate, + RateEndDate = startDate.AddDays(10), + RateSheetId = (int)typeof(int).GetRandomValue() + }); + } + + return rates; + } + + private Rate FakeNewRate(Rate rate, int minValue = int.MinValue, int maxValue = int.MaxValue) + { + var newRate = rate.DeepCopy(); + newRate.RateId = (int)typeof(int).GetRandomValue(); + newRate.Value = new Random().Next(minValue, maxValue); + return newRate; + } + + private List FakeRateHistory(Rate rate, Rate originalRate, int minValue = int.MinValue, int maxValue = int.MaxValue) + { + var rates = new List + { + FakeNewRate(rate, minValue, decimal.ToInt32(rate.Value)), + FakeNewRate(rate, minValue, maxValue), + FakeNewRate(rate, decimal.ToInt32(rate.Value), maxValue) + }; + + var trend = Math.Round(rate.Value / originalRate.Value, 2); + + rates[0].RateStartDate = rate.RateStartDate.AddDays(-30); + rates[0].RateEndDate = rate.RateStartDate.AddDays(-20); + + rates[1].RateStartDate = rate.RateStartDate.AddDays(-20); + rates[1].RateEndDate = rate.RateStartDate.AddDays(-10); + rates[1].Value = Math.Round(rates[0].Value * (trend + 1), 2); + + rates[2].RateStartDate = rate.RateStartDate.AddDays(-10); + rates[2].RateEndDate = rate.RateStartDate; + rates[2].Value = Math.Round(rates[1].Value * (trend - 1), 2); + + return rates; + } +} diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateEvaluationServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateEvaluationServiceTest.cs new file mode 100644 index 0000000..dd8de53 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateEvaluationServiceTest.cs @@ -0,0 +1,511 @@ +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.API.Internal.LoanRateLib.Repositories; +using LCU.API.Internal.LoanRate.Services; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests; + +public class RateEvaluationServiceTest +{ + private readonly Mock _mockUnitOfWork; + private readonly Mock _mockRateEvaluationRepository; + private readonly Mock _mockRateService; + private readonly IRateEvaluationService _service; + + public RateEvaluationServiceTest() + { + _mockRateEvaluationRepository = new Mock(); + _mockUnitOfWork = new Mock(); + _mockRateService = new Mock(); + _service = new RateEvaluationService(_mockUnitOfWork.Object, + _mockRateEvaluationRepository.Object, + _mockRateService.Object); + } + + [Fact] + public async void RateEvaluation_GetAsync_Success() + { + var rateEvaluations = new List + { + new() { RateEvaluationId = 1 }, + new() { RateEvaluationId = 2 }, + new() { RateEvaluationId = 3 } + }; + + _mockRateEvaluationRepository.Setup(x => x.FindAsync()) + .ReturnsAsync(rateEvaluations).Verifiable(); + + var result = (await _service.GetAsync()).ToList(); + Assert.NotNull(result); + Assert.Equal(rateEvaluations.First().RateEvaluationId, result.First().RateEvaluationId); + Assert.Equal(rateEvaluations.Count, result.Count); + _mockRateEvaluationRepository.Verify(); + } + + [Fact] + public async void RateEvaluation_GetAsync_Success_NotFound() + { + List? rateEvaluations = null; + + _mockRateEvaluationRepository.Setup(x => x.FindAsync()) + .ReturnsAsync(rateEvaluations).Verifiable(); + + var result = await _service.GetAsync(); + Assert.NotNull(result); + Assert.Empty(result); + _mockRateEvaluationRepository.Verify(); + } + + [Fact] + public void RateEvaluation_GetAsync_Failure_Exception() + { + _mockRateEvaluationRepository.Setup(x => x.FindAsync()) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.GetAsync()); + + _mockRateEvaluationRepository.Verify(x => x.FindAsync(), Times.Once); + } + + [Fact] + public async void RateEvaluation_GetByIdAsync_Success() + { + var rateEvaluationId = 1; + var rateEvaluation = new RateEvaluation + { + RateEvaluationId = rateEvaluationId + }; + + _mockRateEvaluationRepository.Setup(x => x.FindByIdAsync(rateEvaluationId)) + .ReturnsAsync(rateEvaluation).Verifiable(); + + var result = await _service.GetByIdAsync(rateEvaluationId); + Assert.NotNull(result); + Assert.Equal(rateEvaluation.RateEvaluationId, result.RateEvaluationId); + _mockRateEvaluationRepository.Verify(); + } + + [Fact] + public async void RateEvaluation_GetByIdAsync_Success_NotFound() + { + var rateEvaluationId = 1; + RateEvaluation? rateEvaluation = null; + + _mockRateEvaluationRepository.Setup(x => x.FindByIdAsync(rateEvaluationId)) + .ReturnsAsync(rateEvaluation).Verifiable(); + + var result = await _service.GetByIdAsync(rateEvaluationId); + Assert.Null(result); + _mockRateEvaluationRepository.Verify(); + } + + [Fact] + public void RateEvaluation_GetByIdAsync_Failure_Exception() + { + _mockRateEvaluationRepository.Setup(x => x.FindByIdAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.GetByIdAsync(It.IsAny())); + + _mockRateEvaluationRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async void RateEvaluation_GetRangeByIdAsync_Success() + { + var rateEvaluations = new List + { + new() { RateEvaluationId = 1 }, + new() { RateEvaluationId = 2 }, + new() { RateEvaluationId = 3 } + }; + var ids = rateEvaluations.Select(x => x.RateEvaluationId).ToList(); + + _mockRateEvaluationRepository.Setup(x => x.FindRangeByIdAsync(ids)) + .ReturnsAsync(rateEvaluations).Verifiable(); + + var result = (await _service.GetRangeByIdAsync(ids)).ToList(); + Assert.NotNull(result); + Assert.Equal(rateEvaluations.First().RateEvaluationId, result.First().RateEvaluationId); + Assert.Equal(rateEvaluations.Count, result.Count); + _mockRateEvaluationRepository.Verify(); + } + + [Fact] + public async void RateEvaluation_GetRangeByIdAsync_Success_NotFound() + { + List? rateEvaluations = null; + var ids = new List + { + 1,2,3 + }; + + _mockRateEvaluationRepository.Setup(x => x.FindRangeByIdAsync(ids)) + .ReturnsAsync(rateEvaluations).Verifiable(); + + var result = await _service.GetRangeByIdAsync(ids); + Assert.NotNull(result); + Assert.Empty(result); + _mockRateEvaluationRepository.Verify(); + } + + [Fact] + public void RateEvaluation_GetRangeByIdAsync_Failure_Exception() + { + _mockRateEvaluationRepository.Setup(x => x.FindRangeByIdAsync(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.GetRangeByIdAsync(It.IsAny>())); + + _mockRateEvaluationRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once); + } + + [Fact] + public async void RateEvaluation_SaveAsync_Success() + { + var rateEvaluation = new RateEvaluation + { + RateId = 1, + }; + var rateEvaluationReturned = new RateEvaluation + { + RateEvaluationId = 1, + RateId = 2 + }; + + _mockRateEvaluationRepository.Setup(x => x.AddAsync(rateEvaluation)) + .ReturnsAsync(rateEvaluationReturned).Verifiable(); + + var result = await _service.SaveAsync(rateEvaluation); + Assert.NotNull(result); + Assert.Equal(rateEvaluationReturned.RateEvaluationId, result.RateEvaluationId); + Assert.Equal(rateEvaluationReturned.RateId, result.RateId); + + _mockRateEvaluationRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + _mockRateService.Verify(x => x.ClearCache(), Times.Once); + } + + [Fact] + public void RateEvaluation_SaveAsync_Failure_ReturnedNull() + { + var rateEvaluation = new RateEvaluation + { + RateId = 1 + }; + RateEvaluation? rateEvaluationReturned = null; + + _mockRateEvaluationRepository.Setup(x => x.AddAsync(rateEvaluation)) + .ReturnsAsync(rateEvaluationReturned).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.SaveAsync(rateEvaluation)); + + _mockRateEvaluationRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + _mockRateService.Verify(x => x.ClearCache(), Times.Never); + } + + [Fact] + public void RateEvaluation_SaveAsync_Failure_Exception() + { + _mockRateEvaluationRepository.Setup(x => x.AddAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.SaveAsync(It.IsAny())); + + _mockRateEvaluationRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + _mockRateService.Verify(x => x.ClearCache(), Times.Never); + } + + [Fact] + public async void RateEvaluation_SaveRangeAsync_Success() + { + var rateEvaluations = new List + { + new() { RateId = 1 }, + new() { RateId = 2 }, + new() { RateId = 3 } + }; + + _mockRateEvaluationRepository.Setup(x => x.AddRangeAsync(rateEvaluations)).Verifiable(); + + await _service.SaveRangeAsync(rateEvaluations); + _mockRateEvaluationRepository.Verify(x => + x.AddRangeAsync(It.IsAny>()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + _mockRateService.Verify(x => x.ClearCache(), Times.Once); + } + + [Fact] + public void RateEvaluation_SaveRangeAsync_Failure_Exception() + { + _mockRateEvaluationRepository.Setup(x => x.AddRangeAsync(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.SaveRangeAsync(It.IsAny>())); + + _mockRateEvaluationRepository.Verify(x => + x.AddRangeAsync(It.IsAny>()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + _mockRateService.Verify(x => x.ClearCache(), Times.Never); + } + + [Fact] + public async void RateEvaluation_UpdateAsync_Success() + { + var rateEvaluation = new RateEvaluation + { + RateEvaluationId = 1, + RateId = 1 + }; + + _mockRateEvaluationRepository.Setup(x => x.FindByIdAsync(rateEvaluation.RateEvaluationId)) + .ReturnsAsync(rateEvaluation).Verifiable(); + + var result = await _service.UpdateAsync(rateEvaluation); + Assert.NotNull(result); + Assert.Equal(rateEvaluation.RateEvaluationId, result.RateEvaluationId); + Assert.Equal(rateEvaluation.RateId, result.RateId); + _mockRateEvaluationRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + _mockRateService.Verify(x => x.ClearCache(), Times.Once); + } + + [Fact] + public void RateEvaluation_UpdateAsync_Failure_ReturnedNull() + { + var rateEvaluation = new RateEvaluation + { + RateId = 1 + }; + RateEvaluation? rateEvaluationReturned = null; + + _mockRateEvaluationRepository.Setup(x => x.FindByIdAsync(rateEvaluation.RateEvaluationId)) + .ReturnsAsync(rateEvaluationReturned).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.UpdateAsync(rateEvaluation)); + + _mockRateEvaluationRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + _mockRateService.Verify(x => x.ClearCache(), Times.Never); + } + + [Fact] + public void RateEvaluation_UpdateAsync_Failure_Exception() + { + _mockRateEvaluationRepository.Setup(x => x.FindByIdAsync(It.IsAny())) + .ThrowsAsync(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.UpdateAsync(It.IsAny())); + + _mockRateEvaluationRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + _mockRateService.Verify(x => x.ClearCache(), Times.Never); + } + + [Fact] + public async void RateEvaluation_UpdateRangeAsync_Success() + { + var rateEvaluations = new List + { + new() + { + RateEvaluationId = 1, + RateId = 1 + }, + new() + { + RateEvaluationId = 2, + RateId = 2 + }, + new() + { + RateEvaluationId = 3, + RateId = 3 + } + }; + + _mockRateEvaluationRepository.Setup(x => x.UpdateRange(rateEvaluations)).Verifiable(); + + await _service.UpdateRangeAsync(rateEvaluations); + + _mockRateEvaluationRepository.Verify(x => x.UpdateRange(It.IsAny>()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + _mockRateService.Verify(x => x.ClearCache(), Times.Once); + } + + [Fact] + public void RateEvaluation_UpdateRangeAsync_Failure_Exception() + { + _mockRateEvaluationRepository.Setup(x => x.UpdateRange(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.UpdateRangeAsync(It.IsAny>())); + + _mockRateEvaluationRepository.Verify(x => x.UpdateRange(It.IsAny>()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + _mockRateService.Verify(x => x.ClearCache(), Times.Never); + } + + [Fact] + public async void RateEvaluation_DeleteAsync_Success() + { + var rateEvaluationId = 1; + + var rateEvaluation = new RateEvaluation + { + RateEvaluationId = rateEvaluationId, + RateId = 1 + }; + + _mockRateEvaluationRepository.Setup(x => x.FindByIdAsync(rateEvaluationId)) + .ReturnsAsync(rateEvaluation).Verifiable(); + _mockRateEvaluationRepository.Setup(x => x.Remove(rateEvaluation)).Verifiable(); + + await _service.DeleteAsync(rateEvaluationId); + + _mockRateEvaluationRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once()); + _mockRateEvaluationRepository.Verify(x => x.Remove(It.IsAny()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + _mockRateService.Verify(x => x.ClearCache(), Times.Once); + } + + [Fact] + public void RateEvaluation_DeleteAsync_Failure_ReturnedNull() + { + var rateEvaluationId = 1; + RateEvaluation? rateEvaluation = null; + + _mockRateEvaluationRepository.Setup(x => x.FindByIdAsync(rateEvaluationId)) + .ReturnsAsync(rateEvaluation).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.DeleteAsync(rateEvaluationId)); + + _mockRateEvaluationRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once()); + _mockRateEvaluationRepository.Verify(x => x.Remove(It.IsAny()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + _mockRateService.Verify(x => x.ClearCache(), Times.Never); + } + + [Fact] + public void RateEvaluation_DeleteAsync_Failure_Exception() + { + _mockRateEvaluationRepository.Setup(x => x.FindByIdAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.DeleteAsync(It.IsAny())); + + _mockRateEvaluationRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + _mockRateEvaluationRepository.Verify(x => x.Remove(It.IsAny()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + _mockRateService.Verify(x => x.ClearCache(), Times.Never); + } + + [Fact] + public async void RateEvaluation_DeleteRangeAsync_Success() + { + var rateEvaluationIds = new List + { + 1, + 2, + 3 + }; + var rateEvaluations = new List + { + new() + { + RateEvaluationId = rateEvaluationIds[0], + RateId = 1 + }, + new() + { + RateEvaluationId = rateEvaluationIds[1], + RateId = 2 + }, + new() + { + RateEvaluationId = rateEvaluationIds[2], + RateId = 3 + } + }; + + _mockRateEvaluationRepository.Setup(x => x.FindRangeByIdAsync(rateEvaluationIds)).ReturnsAsync(rateEvaluations).Verifiable(); + _mockRateEvaluationRepository.Setup(x => x.RemoveRange(rateEvaluations)).Verifiable(); + + await _service.DeleteRangeAsync(rateEvaluationIds); + + _mockRateEvaluationRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once()); + _mockRateEvaluationRepository.Verify(x => x.RemoveRange(It.IsAny>()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + _mockRateService.Verify(x => x.ClearCache(), Times.Once); + } + + [Fact] + public void RateEvaluation_DeleteRangeAsync_Failure_ReturnedNull() + { + var rateEvaluationIds = new List + { + 1, + 2, + 3 + }; + List? rateEvaluations = null; + + _mockRateEvaluationRepository.Setup(x => x.FindRangeByIdAsync(rateEvaluationIds)) + .ReturnsAsync(rateEvaluations).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.DeleteRangeAsync(rateEvaluationIds)); + + _mockRateEvaluationRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once()); + _mockRateEvaluationRepository.Verify(x => x.RemoveRange(It.IsAny>()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + _mockRateService.Verify(x => x.ClearCache(), Times.Never); + } + + [Fact] + public void RateEvaluation_DeleteRangeAsync_Failure_Exception() + { + _mockRateEvaluationRepository.Setup(x => x.RemoveRange(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.DeleteRangeAsync(It.IsAny>())); + + _mockRateEvaluationRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once()); + _mockRateEvaluationRepository.Verify(x => x.RemoveRange(It.IsAny>()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + _mockRateService.Verify(x => x.ClearCache(), Times.Never); + } + + [Fact] + public async void RateEvaluation_CountByEvaluationDetailIdAsync_Success() + { + var evaluationDetailId = DateTime.Now.Millisecond; + var count = DateTime.Now.Minute; + + _mockRateEvaluationRepository.Setup(x => x.CountByEvaluationDetailIdAsync(evaluationDetailId)).ReturnsAsync(count).Verifiable(); + + await _service.CountByEvaluationDetailIdAsync(evaluationDetailId); + + _mockRateEvaluationRepository.Verify(x => x.CountByEvaluationDetailIdAsync(It.IsAny()), Times.Once()); + } + + [Fact] + public void RateEvaluation_CountByEvaluationDetailIdAsync_Failure_Exception() + { + _mockRateEvaluationRepository.Setup(x => x.CountByEvaluationDetailIdAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.CountByEvaluationDetailIdAsync(It.IsAny())); + + _mockRateEvaluationRepository.Verify(x => x.CountByEvaluationDetailIdAsync(It.IsAny()), Times.Once()); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateServiceTest.cs new file mode 100644 index 0000000..8f72eb5 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateServiceTest.cs @@ -0,0 +1,649 @@ +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.API.Internal.LoanRateLib.Repositories; +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.LcuEvaluationModels.Enums; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests; + +public class RateServiceTest +{ + private readonly Mock _mockRateRepository; + private readonly Mock _mockRateSheetService; + private readonly Mock _mockLoadTestRunInfoService; + private readonly Mock _mockUnitOfWork; + private readonly IRateService _service; + + public RateServiceTest() + { + _mockRateRepository = new Mock(); + _mockRateSheetService = new Mock(); + _mockLoadTestRunInfoService = new Mock(); + _mockUnitOfWork = new Mock(); + _service = new RateService(_mockUnitOfWork.Object, + _mockRateRepository.Object, + _mockRateSheetService.Object, + _mockLoadTestRunInfoService.Object); + } + + [Fact] + public async void Rate_GetAsync_Success() + { + var rates = new List + { + new() { RateId = 1 }, + new() { RateId = 2 }, + new() { RateId = 3 } + }; + + _mockRateRepository.Setup(x => x.FindAsync()) + .ReturnsAsync(rates).Verifiable(); + + var result = (await _service.GetAsync()).ToList(); + Assert.NotNull(result); + Assert.Equal(rates.First().RateId, result.First().RateId); + Assert.Equal(rates.Count, result.Count); + _mockRateRepository.Verify(); + } + + [Fact] + public async void Rate_GetAsync_Success_NotFound() + { + List? rates = null; + + _mockRateRepository.Setup(x => x.FindAsync()) + .ReturnsAsync(rates).Verifiable(); + + var result = await _service.GetAsync(); + Assert.NotNull(result); + Assert.Empty(result); + _mockRateRepository.Verify(); + } + + [Fact] + public void Rate_GetAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.FindAsync()) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.GetAsync()); + + _mockRateRepository.Verify(x => x.FindAsync(), Times.Once); + } + + [Fact] + public async void Rate_GetByIdAsync_Success() + { + var rateId = 1; + var rate = new Rate + { + RateId = rateId + }; + + _mockRateRepository.Setup(x => x.FindByIdAsync(rateId)) + .ReturnsAsync(rate).Verifiable(); + + var result = await _service.GetByIdAsync(rateId); + Assert.NotNull(result); + Assert.Equal(rate.RateId, result.RateId); + _mockRateRepository.Verify(); + } + + [Fact] + public async void Rate_GetByIdAsync_Success_NotFound() + { + var rateId = 1; + Rate? rate = null; + + _mockRateRepository.Setup(x => x.FindByIdAsync(rateId)) + .ReturnsAsync(rate).Verifiable(); + + var result = await _service.GetByIdAsync(rateId); + Assert.Null(result); + _mockRateRepository.Verify(); + } + + [Fact] + public void Rate_GetByIdAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.FindByIdAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.GetByIdAsync(It.IsAny())); + + _mockRateRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async void Rate_GetByDateAsync_Success() + { + var rateId = 1; + var rates = new List{ + new Rate + { + RateId = rateId + } + }; + var date = Convert.ToDateTime("1/1/2023"); + _mockRateRepository.Setup(x => x.FindByDateAsync(date)) + .ReturnsAsync(rates).Verifiable(); + + var results = await _service.GetByDateAsync(date); + Assert.NotNull(results); + Assert.NotEmpty(results); + var result = results.FirstOrDefault(); + Assert.NotNull(result); + Assert.Equal(rateId, result.RateId); + _mockRateRepository.Verify(); + } + + [Fact] + public async void Rate_GetByDateAsync_Success_NotFound() + { + List? rate = null; + var date = Convert.ToDateTime("1/1/2023"); + _mockRateRepository.Setup(x => x.FindByDateAsync(date)) + .ReturnsAsync(rate).Verifiable(); + + var results = await _service.GetByDateAsync(date); + Assert.NotNull(results); + Assert.Empty(results); + _mockRateRepository.Verify(); + } + + [Fact] + public void Rate_GetByDateAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.FindByDateAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.GetByDateAsync(It.IsAny())); + + _mockRateRepository.Verify(x => x.FindByDateAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async void Rate_GetRateHistoryAsync_Success() + { + var rate = new Rate + { + RateId = 1, + Column = "A", + Row = "2", + RateSheetId = 1 + }; + var rateHistory = new List + { + new() + { + Column = rate.Column, + Row = rate.Row, + RateSheetId = rate.RateSheetId + }, + new() + { + Column = rate.Column, + Row = rate.Row, + RateSheetId = rate.RateSheetId + }, + new() + { + Column = rate.Column, + Row = rate.Row, + RateSheetId = rate.RateSheetId + } + }; + _mockRateRepository.Setup(x => x.FindRateHistoryAsync(rate)) + .ReturnsAsync(rateHistory).Verifiable(); + + var results = (await _service.GetRateHistory(rate))?.ToList(); + Assert.NotNull(results); + Assert.NotEmpty(results); + + var result = results.FirstOrDefault(); + Assert.NotNull(result); + Assert.Equal(rate.RateSheetId, result.RateSheetId); + _mockRateRepository.Verify(); + } + + [Fact] + public async void Rate_GetRateHistoryAsync_Success_NotFound() + { + Rate? rate = null; + List? rateHistory = null; + _mockRateRepository.Setup(x => x.FindRateHistoryAsync(rate)) + .ReturnsAsync(rateHistory).Verifiable(); + + var results = await _service.GetRateHistory(rate); + Assert.NotNull(results); + Assert.Empty(results); + _mockRateRepository.Verify(); + } + + [Fact] + public void Rate_GetRateHistoryAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.FindRateHistoryAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.GetRateHistory(It.IsAny())); + + _mockRateRepository.Verify(x => x.FindRateHistoryAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async void Rate_GetRangeByIdAsync_Success() + { + var rates = new List + { + new() { RateId = 1 }, + new() { RateId = 2 }, + new() { RateId = 3 } + }; + var ids = rates.Select(x => x.RateId).ToList(); + + _mockRateRepository.Setup(x => x.FindRangeByIdAsync(ids)) + .ReturnsAsync(rates).Verifiable(); + + var results = (await _service.GetRangeByIdAsync(ids)).ToList(); + Assert.NotNull(results); + Assert.Equal(rates.First().RateId, results.First().RateId); + Assert.Equal(rates.Count, results.Count); + _mockRateRepository.Verify(); + } + + [Fact] + public async void Rate_GetRangeByIdAsync_Success_NotFound() + { + List? rates = null; + var ids = new List + { + 1,2,3 + }; + + _mockRateRepository.Setup(x => x.FindRangeByIdAsync(ids)) + .ReturnsAsync(rates).Verifiable(); + + var result = await _service.GetRangeByIdAsync(ids); + Assert.NotNull(result); + Assert.Empty(result); + _mockRateRepository.Verify(); + } + + [Fact] + public void Rate_GetRangeByIdAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.FindRangeByIdAsync(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.GetRangeByIdAsync(It.IsAny>())); + + _mockRateRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once); + } + + [Fact] + public async void Rate_GetByDateEvaluationIdAndEvaluationTypeAsync_Success() + { + var rateId = 1; + var rateSheetId = 1; + var date = Convert.ToDateTime("1/1/2023"); + var rates = new List{ + new Rate + { + RateId = rateId, + RateSheetId = rateSheetId, + RateStartDate = date + } + }; + var evaluationIds = new List { 10 }; + var evaluationType = EvaluationTypes.Rule; + _mockRateRepository.Setup(x => x.FindByDateEvaluationIdsAndEvaluationTypeAsync(date, evaluationIds, evaluationType)) + .ReturnsAsync(rates).Verifiable(); + + var results = await _service.GetByDateEvaluationIdAndEvaluationTypeAsync(date, evaluationIds, evaluationType); + Assert.NotNull(results); + Assert.NotEmpty(results); + var result = results.FirstOrDefault(); + Assert.NotNull(result); + Assert.Equal(rateId, result.RateId); + _mockRateRepository.Verify(); + } + + [Fact] + public async void Rate_GetByDateEvaluationIdAndEvaluationTypeAsync_Success_NotFound() + { + var date = Convert.ToDateTime("1/1/2023"); + List? rates = null; + var evaluationIds = new List { 10 }; + var evaluationType = EvaluationTypes.Rule; + _mockRateRepository.Setup(x => x.FindByDateEvaluationIdsAndEvaluationTypeAsync(date, evaluationIds, evaluationType)) + .ReturnsAsync(rates).Verifiable(); + + var result = await _service.GetByDateEvaluationIdAndEvaluationTypeAsync(date, evaluationIds, evaluationType); + Assert.NotNull(result); + Assert.Empty(result); + _mockRateRepository.Verify(); + } + + [Fact] + public void Rate_GetByDateEvaluationIdAndEvaluationTypeAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.FindByDateEvaluationIdsAndEvaluationTypeAsync(It.IsAny(), It.IsAny>(), It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.GetByDateEvaluationIdAndEvaluationTypeAsync(It.IsAny(), It.IsAny>(), It.IsAny())); + + _mockRateRepository.Verify(x => x.FindByDateEvaluationIdsAndEvaluationTypeAsync(It.IsAny(), It.IsAny>(), It.IsAny()), Times.Once); + } + + [Fact] + public async void Rate_SaveAsync_Success() + { + var rate = new Rate + { + Value = 4.59m + }; + var RateReturned = new Rate + { + RateId = 1, + Value = 4.9m + }; + + _mockRateRepository.Setup(x => x.AddAsync(rate)) + .ReturnsAsync(RateReturned).Verifiable(); + + var result = await _service.SaveAsync(rate); + Assert.NotNull(result); + Assert.Equal(RateReturned.RateId, result.RateId); + Assert.Equal(RateReturned.Value, result.Value); + + _mockRateRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void Rate_SaveAsync_Failure_ReturnedNull() + { + var rate = new Rate + { + Value = 4.59m + }; + Rate? rateReturned = null; + + _mockRateRepository.Setup(x => x.AddAsync(rate)) + .ReturnsAsync(rateReturned).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.SaveAsync(rate)); + + _mockRateRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public void Rate_SaveAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.AddAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.SaveAsync(It.IsAny())); + + _mockRateRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public async void Rate_SaveRangeAsync_Success() + { + var rates = new List + { + new() { Value = 4.59m }, + new() { Value = 4.9m }, + new() { Value = 4m } + }; + + _mockRateRepository.Setup(x => x.AddRangeAsync(rates)).Verifiable(); + + await _service.SaveRangeAsync(rates); + _mockRateRepository.Verify(x => + x.AddRangeAsync(It.IsAny>()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void Rate_SaveRangeAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.AddRangeAsync(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.SaveRangeAsync(It.IsAny>())); + + _mockRateRepository.Verify(x => + x.AddRangeAsync(It.IsAny>()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public async void Rate_UpdateAsync_Success() + { + var rate = new Rate + { + RateId = 1, + Value = 4.59m + }; + + _mockRateRepository.Setup(x => x.FindByIdAsync(rate.RateId)) + .ReturnsAsync(rate).Verifiable(); + + var result = await _service.UpdateAsync(rate); + Assert.NotNull(result); + Assert.Equal(rate.RateId, result.RateId); + Assert.Equal(rate.Value, result.Value); + _mockRateRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void Rate_UpdateAsync_Failure_ReturnedNull() + { + var rate = new Rate + { + Value = 4.59m + }; + Rate? rateReturned = null; + + _mockRateRepository.Setup(x => x.FindByIdAsync(rate.RateId)) + .ReturnsAsync(rateReturned).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.UpdateAsync(rate)); + + _mockRateRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public void Rate_UpdateAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.FindByIdAsync(It.IsAny())) + .ThrowsAsync(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.UpdateAsync(It.IsAny())); + + _mockRateRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public async void Rate_UpdateRangeAsync_Success() + { + var rates = new List + { + new() + { + RateId = 1, + Value = 4.59m + }, + new() + { + RateId = 2, + Value = 4.9m + }, + new() + { + RateId = 3, + Value = 4m + } + }; + + _mockRateRepository.Setup(x => x.UpdateRange(rates)).Verifiable(); + + await _service.UpdateRangeAsync(rates); + + _mockRateRepository.Verify(x => x.UpdateRange(It.IsAny>()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void Rate_UpdateRangeAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.UpdateRange(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.UpdateRangeAsync(It.IsAny>())); + + _mockRateRepository.Verify(x => x.UpdateRange(It.IsAny>()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public async void Rate_DeleteAsync_Success() + { + var rateId = 1; + + var rate = new Rate + { + RateId = rateId, + Value = 4.59m + }; + + _mockRateRepository.Setup(x => x.FindByIdAsync(rateId)) + .ReturnsAsync(rate).Verifiable(); + _mockRateRepository.Setup(x => x.Remove(rate)).Verifiable(); + + await _service.DeleteAsync(rateId); + + _mockRateRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once()); + _mockRateRepository.Verify(x => x.Remove(It.IsAny()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void Rate_DeleteAsync_Failure_ReturnedNull() + { + var rateId = 1; + Rate? rate = null; + + _mockRateRepository.Setup(x => x.FindByIdAsync(rateId)) + .ReturnsAsync(rate).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.DeleteAsync(rateId)); + + _mockRateRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once()); + _mockRateRepository.Verify(x => x.Remove(It.IsAny()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public void Rate_DeleteAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.FindByIdAsync(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.DeleteAsync(It.IsAny())); + + _mockRateRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Once); + _mockRateRepository.Verify(x => x.Remove(It.IsAny()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public async void Rate_DeleteRangeAsync_Success() + { + var rateIds = new List + { + 1, + 2, + 3 + }; + var rates = new List + { + new() + { + RateId = rateIds[0], + Value = 4.59m + }, + new() + { + RateId = rateIds[1], + Value = 4.9m + }, + new() + { + RateId = rateIds[2], + Value = 4m + } + }; + + _mockRateRepository.Setup(x => x.FindRangeByIdAsync(rateIds)).ReturnsAsync(rates).Verifiable(); + _mockRateRepository.Setup(x => x.RemoveRange(rates)).Verifiable(); + + await _service.DeleteRangeAsync(rateIds); + + _mockRateRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once()); + _mockRateRepository.Verify(x => x.RemoveRange(It.IsAny>()), Times.Once()); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Once); + } + + [Fact] + public void Rate_DeleteRangeAsync_Failure_ReturnedNull() + { + var rateIds = new List + { + 1, + 2, + 3 + }; + List? rates = null; + + _mockRateRepository.Setup(x => x.FindRangeByIdAsync(rateIds)) + .ReturnsAsync(rates).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.DeleteRangeAsync(rateIds)); + + _mockRateRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once()); + _mockRateRepository.Verify(x => x.RemoveRange(It.IsAny>()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } + + [Fact] + public void Rate_DeleteRangeAsync_Failure_Exception() + { + _mockRateRepository.Setup(x => x.RemoveRange(It.IsAny>())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => + await _service.DeleteRangeAsync(It.IsAny>())); + + _mockRateRepository.Verify(x => x.FindRangeByIdAsync(It.IsAny>()), Times.Once()); + _mockRateRepository.Verify(x => x.RemoveRange(It.IsAny>()), Times.Never); + _mockUnitOfWork.Verify(x => x.CompleteAsync(), Times.Never); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateSheetCellServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateSheetCellServiceTest.cs new file mode 100644 index 0000000..40ae770 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateSheetCellServiceTest.cs @@ -0,0 +1,19 @@ +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.API.Internal.LoanRateLib.Repositories; +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.LcuClassPatterns.CrudTests.Tests; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests; + +public class RateSheetCellServiceTest : CrudServiceTest +{ + private readonly IRateSheetCellService _service; + private readonly Mock _mockRateSheetCellRepository; + + public RateSheetCellServiceTest() + { + _mockRateSheetCellRepository = new Mock(); + _service = new RateSheetCellService(_mockRateSheetCellRepository.Object); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateSheetServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateSheetServiceTest.cs new file mode 100644 index 0000000..3c27014 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateSheetServiceTest.cs @@ -0,0 +1,190 @@ +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.API.Internal.LoanRateLib.Repositories; +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.LcuClassPatterns.CrudTests.Tests; +using LCU.Library.Internal.LcuClassPatterns.CrudTests.Utilities; +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests; + +public class RateSheetServiceTest : CrudServiceTest +{ + private readonly IRateSheetService _service; + private readonly Mock _mockRateSheetRepository; + + public RateSheetServiceTest() + { + _mockRateSheetRepository = new Mock(); + _service = new RateSheetService(_mockRateSheetRepository.Object); + } + + [Fact] + public async void RateSheet_GetIncludeAllAsync_Success() + { + var rateSheets = new List + { + new () + { + RateSheetId = (int)typeof(int).GetRandomValue(), + RateType = (RateTypes)typeof(RateTypes).GetRandomValue(), + Rates = new List + { + new() { RateId = (int)typeof(int).GetRandomValue() }, + new() { RateId = (int)typeof(int).GetRandomValue() }, + new() { RateId = (int)typeof(int).GetRandomValue() } + } + } + }; + + _mockRateSheetRepository.Setup(x => x.FindIncludeAllAsync()) + .ReturnsAsync(rateSheets).Verifiable(); + + var result = (await _service.GetIncludeAllAsync())?.ToList(); + Assert.NotNull(result); + Assert.Equal(rateSheets.First().RateSheetId, result.First().RateSheetId); + Assert.Equal(rateSheets.Count, result.Count); + _mockRateSheetRepository.Verify(); + } + + [Fact] + public async void RateSheet_GetIncludeAllAsync_Success_NotFound() + { + List? rateSheets = null; + + _mockRateSheetRepository.Setup(x => x.FindIncludeAllAsync()) + .ReturnsAsync(rateSheets).Verifiable(); + + var result = await _service.GetIncludeAllAsync(); + Assert.NotNull(result); + Assert.Empty(result); + _mockRateSheetRepository.Verify(); + } + + [Fact] + public void Rate_GetAsync_Failure_Exception() + { + _mockRateSheetRepository.Setup(x => x.FindAsync()) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.GetAsync()); + + _mockRateSheetRepository.Verify(x => x.FindAsync(), Times.Once); + } + + [Fact] + public async void RateSheet_GetRatesByRateTypeAsync_Success() + { + var rateType = (RateTypes)typeof(RateTypes).GetRandomValue(); + var rateSheet = new RateSheet + { + RateSheetId = (int)typeof(int).GetRandomValue(), + RateType = (RateTypes)typeof(RateTypes).GetRandomValue(), + Rates = new List + { + new() { RateId = (int)typeof(int).GetRandomValue() }, + new() { RateId = (int)typeof(int).GetRandomValue() }, + new() { RateId = (int)typeof(int).GetRandomValue() } + } + }; + _mockRateSheetRepository.Setup(x => x.FindRatesByRateType(rateType)) + .ReturnsAsync(rateSheet.Rates).Verifiable(); + + var result = (await _service.GetRatesByRateTypeAsync(rateType))?.ToList(); + Assert.NotNull(result); + Assert.Equal(rateSheet.Rates.First().RateId, result.First().RateId); + Assert.Equal(rateSheet.Rates.Count, result.Count); + _mockRateSheetRepository.Verify(); + } + + [Fact] + public async void RateSheet_GetRatesByRateTypeAsync_Success_NotFound() + { + List? rates = null; + + _mockRateSheetRepository.Setup(x => x.FindRatesByRateType(It.IsAny())) + .ReturnsAsync(rates).Verifiable(); + + var result = await _service.GetRatesByRateTypeAsync(It.IsAny()); + Assert.NotNull(result); + Assert.Empty(result); + _mockRateSheetRepository.Verify(); + } + + [Fact] + public void RateSheet_GetRatesByRateTypeAsync_Failure_Exception() + { + _mockRateSheetRepository.Setup(x => x.FindRatesByRateType(It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.GetRatesByRateTypeAsync(It.IsAny())); + + _mockRateSheetRepository.Verify(x => x.FindRatesByRateType(It.IsAny()), Times.Once); + } + + [Fact] + public async void RateSheet_GetRatesByDateAndRateTypeAsync_Success() + { + var effectiveDate = (DateTime)typeof(DateTime).GetRandomValue(); + var rateType = (RateTypes)typeof(RateTypes).GetRandomValue(); + var rateSheet = new RateSheet + { + RateSheetId = (int)typeof(int).GetRandomValue(), + RateType = (RateTypes)typeof(RateTypes).GetRandomValue(), + Rates = new List + { + new() + { + RateId = (int)typeof(int).GetRandomValue(), + RateStartDate = effectiveDate.AddDays(-2), + RateEndDate = effectiveDate.AddDays(2) + }, + new() + { + RateId = (int)typeof(int).GetRandomValue(), + RateStartDate = effectiveDate.AddDays(-1), + RateEndDate = effectiveDate.AddDays(3) + }, + new() + { + RateId = (int)typeof(int).GetRandomValue(), + RateStartDate = effectiveDate.AddDays(-5), + RateEndDate = effectiveDate.AddDays(2) + } + } + }; + _mockRateSheetRepository.Setup(x => x.FindRatesByDateAndRateType(effectiveDate, rateType)) + .ReturnsAsync(rateSheet.Rates).Verifiable(); + + var result = (await _service.GetRatesByDateAndRateTypeAsync(effectiveDate, rateType))?.ToList(); + Assert.NotNull(result); + Assert.Equal(rateSheet.Rates.First().RateId, result.First().RateId); + Assert.Equal(rateSheet.Rates.Count, result.Count); + _mockRateSheetRepository.Verify(); + } + + [Fact] + public async void RateSheet_GetRatesByDateAndRateTypeAsync_Success_NotFound() + { + List? rates = null; + + _mockRateSheetRepository.Setup(x => x.FindRatesByDateAndRateType(It.IsAny(), It.IsAny())) + .ReturnsAsync(rates).Verifiable(); + + var result = await _service.GetRatesByDateAndRateTypeAsync(It.IsAny(), It.IsAny()); + Assert.NotNull(result); + Assert.Empty(result); + _mockRateSheetRepository.Verify(); + } + + [Fact] + public void RateSheet_GetRatesByDateAndRateTypeAsync_Failure_Exception() + { + _mockRateSheetRepository.Setup(x => x.FindRatesByDateAndRateType(It.IsAny(), It.IsAny())) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.GetRatesByDateAndRateTypeAsync(It.IsAny(), It.IsAny())); + + _mockRateSheetRepository.Verify(x => x.FindRatesByDateAndRateType(It.IsAny(), It.IsAny()), Times.Once); + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateUploadInfoServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateUploadInfoServiceTest.cs new file mode 100644 index 0000000..baf9c72 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/RateUploadInfoServiceTest.cs @@ -0,0 +1,317 @@ +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.API.Internal.LoanRateLib.Repositories; +using LCU.API.Internal.LoanRate.Services; +using LCU.Library.Internal.Exceptions; +using LCU.Library.Internal.LcuClassPatterns.CrudTests.Tests; +using LCU.Library.Internal.LcuClassPatterns.CrudTests.Utilities; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.TypeExtensions.ObjectExtensions; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests; + +public class RateUploadInfoServiceTest : CrudServiceTest +{ + private readonly Mock _mockRateUploadInfoRepository = new(); + private readonly Mock _mockRateService = new(); + private readonly IRateUploadInfoService _service; + + public RateUploadInfoServiceTest() + { + _service = new RateUploadInfoService(_mockRateUploadInfoRepository.Object, _mockRateService.Object); + } + + [Fact] + public async void RateUploadInfo_GetLatest_Success() + { + var version = (int)typeof(int).GetRandomValue(); + var dateNow = DateTime.Now; + var rateUploadInfo = new RateUploadInfo + { + RateUploadInfoId = (int)typeof(int).GetRandomValue(), + EffectiveDate = dateNow, + Version = version + }; + + _mockRateUploadInfoRepository.Setup(x => x.FindLatestAsync()).ReturnsAsync(rateUploadInfo).Verifiable(); + + var result = await _service.GetLatestAsync(); + + Assert.NotNull(result); + Assert.Equal(rateUploadInfo.Version, result.Version); + _mockRateUploadInfoRepository.Verify(x => x.FindLatestAsync(), Times.Once); + } + + [Fact] + public async void RateUploadInfo_GetLatest_Success_NotFound() + { + RateUploadInfo? previousRateUploadInfo = null; + + _mockRateUploadInfoRepository.Setup(x => x.FindLatestAsync()) + .ReturnsAsync(previousRateUploadInfo).Verifiable(); + + var result = await _service.GetLatestAsync(); + Assert.Null(result); + _mockRateUploadInfoRepository.Verify(x => x.FindLatestAsync(), Times.Once); + } + + [Fact] + public void RateUploadInfo_GetLatest_Failure_Exception() + { + _mockRateUploadInfoRepository.Setup(x => x.FindLatestAsync()) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.GetLatestAsync()); + + _mockRateUploadInfoRepository.Verify(x => x.FindAsync(), Times.Never); + } + + [Fact] + public async void RateUploadInfo_GetByVersion_Success() + { + var version = (int)typeof(int).GetRandomValue(); + var effectiveDate = (DateTime)typeof(DateTime).GetRandomValue(); + var rateUploadInfo = new RateUploadInfo + { + RateUploadInfoId = (int)typeof(int).GetRandomValue(), + Version = version + }; + + _mockRateUploadInfoRepository.Setup(x => x.FindByVersionAsync(version, effectiveDate)) + .ReturnsAsync(rateUploadInfo).Verifiable(); + + var result = await _service.GetByVersionAsync(version, effectiveDate); + + Assert.NotNull(result); + Assert.Equal(rateUploadInfo.RateUploadInfoId, result.RateUploadInfoId); + Assert.Equal(rateUploadInfo.Version, result.Version); + _mockRateUploadInfoRepository.Verify(x => x.FindByVersionAsync(version, effectiveDate), Times.Once); + } + + [Fact] + public async void RateUploadInfo_GetByVersion_Success_NotFound() + { + var version = (int)typeof(int).GetRandomValue(); + var effectiveDate = (DateTime)typeof(DateTime).GetRandomValue(); + RateUploadInfo rateUploadInfo = null; + + _mockRateUploadInfoRepository.Setup(x => x.FindByVersionAsync(version, effectiveDate)) + .ReturnsAsync(rateUploadInfo).Verifiable(); + + var result = await _service.GetByVersionAsync(version, effectiveDate); + Assert.Null(result); + _mockRateUploadInfoRepository.Verify(x => x.FindByVersionAsync(version, effectiveDate), Times.Once); + } + + [Fact] + public void RateUploadInfo_GetByVersion_Failure_Exception() + { + var version = (int)typeof(int).GetRandomValue(); + var effectiveDate = (DateTime)typeof(DateTime).GetRandomValue(); + RateUploadInfo rateUploadInfo = null; + + _mockRateUploadInfoRepository.Setup(x => x.FindByVersionAsync(version, effectiveDate)) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.GetByVersionAsync(version, effectiveDate)); + + _mockRateUploadInfoRepository.Verify(x => x.FindAsync(), Times.Never); + } + + [Fact] + public async void RateUploadInfo_GetLatestVersionByDate_Success() + { + var version = (int)typeof(int).GetRandomValue(); + var dateNow = DateTime.Now; + var rateUploadInfo = new RateUploadInfo + { + RateUploadInfoId = (int)typeof(int).GetRandomValue(), + EffectiveDate = dateNow, + Version = version + }; + + _mockRateUploadInfoRepository.Setup(x => x.FindLatestVersionOnDate(dateNow)).ReturnsAsync(rateUploadInfo).Verifiable(); + + var result = await _service.GetLatestVersionOnDate(dateNow); + + Assert.NotNull(result); + Assert.Equal(rateUploadInfo.Version, result.Version); + _mockRateUploadInfoRepository.Verify(x => x.FindLatestVersionOnDate(dateNow), Times.Once); + } + + [Fact] + public async void RateUploadInfo_GetLatestVersionByDate_Success_NotFound() + { + RateUploadInfo? previousRateUploadInfo = null; + var dateNow = DateTime.Now; + + _mockRateUploadInfoRepository.Setup(x => x.FindLatestVersionOnDate(dateNow)) + .ReturnsAsync(previousRateUploadInfo).Verifiable(); + + var result = await _service.GetLatestVersionOnDate(dateNow); + Assert.Null(result); + _mockRateUploadInfoRepository.Verify(x => x.FindLatestVersionOnDate(dateNow), Times.Once); + } + + [Fact] + public void RateUploadInfo_GetLatestVersionByDate_Failure_Exception() + { + var dateNow = DateTime.Now; + + _mockRateUploadInfoRepository.Setup(x => x.FindLatestVersionOnDate(dateNow)) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.GetLatestVersionOnDate(dateNow)); + + _mockRateUploadInfoRepository.Verify(x => x.FindAsync(), Times.Never); + } + + [Fact] + public async void RateUploadInfo_ComplexSave_Success() + { + var effectiveDate = (DateTime)typeof(DateTime).GetRandomValue(); + var version = (int)typeof(int).GetRandomValue(); + + var rates = new List + { + new() { RateId = (int)typeof(int).GetRandomValue() }, + new() { RateId = (int)typeof(int).GetRandomValue() }, + new() { RateId = (int)typeof(int).GetRandomValue() } + }; + + var request = new RateUploadInfoRequest + { + EffectiveDate = effectiveDate, + Rates = rates + }; + + var originalRateUploadInfo = new RateUploadInfo + { + RateUploadInfoId = (int)typeof(int).GetRandomValue(), + Version = version, + EffectiveDate = effectiveDate.AddDays(-25) + }; + + var rateUploadInfo = new RateUploadInfo + { + RateUploadInfoId = (int)typeof(int).GetRandomValue(), + Version = version + 1, + EffectiveDate = effectiveDate, + Filename = "Complex Save Test", + ClosedRates = rates, + Rates = rates.DeepCopy() + }; + + _mockRateUploadInfoRepository.Setup(x => x.FindLatestVersionOnDate(effectiveDate)) + .ReturnsAsync(originalRateUploadInfo).Verifiable(); + + _mockRateUploadInfoRepository.Setup(x => x.AddAsync(It.IsAny())) + .ReturnsAsync(rateUploadInfo).Verifiable(); + + var result = await _service.ComplexSave(request); + + Assert.NotNull(result); + Assert.Equal(rateUploadInfo.RateUploadInfoId, result.RateUploadInfoId); + Assert.Equal(rateUploadInfo.Version, result.Version); + Assert.Equal(rateUploadInfo.Rates.Count, result.ClosedRates?.Count); + _mockRateUploadInfoRepository.Verify(x => x.FindLatestVersionOnDate(effectiveDate), Times.Once); + _mockRateUploadInfoRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + } + + [Fact] + public void RateUploadInfo_ComplexSave_Failure_Exception() + { + var effectiveDate = (DateTime)typeof(DateTime).GetRandomValue(); + RateUploadInfoRequest request = null; + + _mockRateUploadInfoRepository.Setup(x => x.FindLatestVersionOnDate(effectiveDate)) + .Throws(new Exception("Test")).Verifiable(); + + Assert.ThrowsAsync(async () => await _service.ComplexSave(request)); + + _mockRateUploadInfoRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Never); + } + + [Fact] + public async void RateUploadInfo_ComplexDelete_Success() + { + var closedRates = new List + { + new () { RateId = (int)typeof(int).GetRandomValue() }, + new () { RateId = (int)typeof(int).GetRandomValue() }, + new() { RateId = (int)typeof(int).GetRandomValue() } + }; + var rateUploadInfo = new RateUploadInfo + { + RateUploadInfoId = (int)typeof(int).GetRandomValue(), + Rates = new List + { + new () { RateId = (int)typeof(int).GetRandomValue() }, + new () { RateId = (int)typeof(int).GetRandomValue() }, + new() { RateId = (int)typeof(int).GetRandomValue() } + } + }; + + _mockRateUploadInfoRepository.Setup(x => x.FindLatestAsync()) + .ReturnsAsync(rateUploadInfo).Verifiable(); + _mockRateService.Setup(x => x.GetByClosingRateUploadInfoIdAsync(rateUploadInfo.RateUploadInfoId)) + .ReturnsAsync(closedRates).Verifiable(); + _mockRateUploadInfoRepository.Setup(x => x.FindByIdAsync(rateUploadInfo.RateUploadInfoId)) + .ReturnsAsync(rateUploadInfo).Verifiable(); + + await _service.ComplexDelete(rateUploadInfo.RateUploadInfoId); + + _mockRateUploadInfoRepository.Verify(x => x.FindLatestAsync(), Times.Once); + _mockRateService.Verify(x => x.GetByClosingRateUploadInfoIdAsync(rateUploadInfo.RateUploadInfoId), Times.Once); + _mockRateService.Verify(x => x.UpdateRangeAsync(It.IsAny>()), Times.Once); + _mockRateUploadInfoRepository.Verify(x => x.FindByIdAsync(rateUploadInfo.RateUploadInfoId), Times.Once); + _mockRateUploadInfoRepository.Verify(x => x.RemoveAsync(rateUploadInfo), Times.Once); + } + + [Fact] + public async void RateUploadInfo_ComplexDelete_Success_NoClosedRates() + { + var closedRates = new List(); + var rateUploadInfo = new RateUploadInfo + { + RateUploadInfoId = (int)typeof(int).GetRandomValue(), + Rates = new List + { + new () { RateId = (int)typeof(int).GetRandomValue() }, + new () { RateId = (int)typeof(int).GetRandomValue() }, + new() { RateId = (int)typeof(int).GetRandomValue() } + } + }; + + _mockRateUploadInfoRepository.Setup(x => x.FindLatestAsync()) + .ReturnsAsync(rateUploadInfo).Verifiable(); + _mockRateService.Setup(x => x.GetByClosingRateUploadInfoIdAsync(rateUploadInfo.RateUploadInfoId)) + .ReturnsAsync(closedRates).Verifiable(); + _mockRateUploadInfoRepository.Setup(x => x.FindByIdAsync(rateUploadInfo.RateUploadInfoId)) + .ReturnsAsync(rateUploadInfo).Verifiable(); + + await _service.ComplexDelete(rateUploadInfo.RateUploadInfoId); + + _mockRateUploadInfoRepository.Verify(x => x.FindLatestAsync(), Times.Once); + _mockRateService.Verify(x => x.GetByClosingRateUploadInfoIdAsync(rateUploadInfo.RateUploadInfoId), Times.Once); + _mockRateService.Verify(x => x.UpdateRangeAsync(It.IsAny>()), Times.Never); + _mockRateUploadInfoRepository.Verify(x => x.FindByIdAsync(rateUploadInfo.RateUploadInfoId), Times.Once); + _mockRateUploadInfoRepository.Verify(x => x.RemoveAsync(rateUploadInfo), Times.Once); + } + + [Fact] + public async void RateUploadInfo_ComplexDelete_Failure_Exception() + { + var rateUploadInfoId = (int)typeof(int).GetRandomValue(); + + _mockRateUploadInfoRepository.Setup(x => x.FindLatestAsync()) + .Throws(new Exception("Test")).Verifiable(); + + await Assert.ThrowsAsync(async () => await _service.ComplexDelete(rateUploadInfoId)); + + _mockRateUploadInfoRepository.Verify(x => x.FindLatestAsync(), Times.Once); + _mockRateUploadInfoRepository.Verify(x => x.FindPreviousVersion(It.IsAny()), Times.Never); + _mockRateUploadInfoRepository.Verify(x => x.FindByIdAsync(It.IsAny()), Times.Never); + _mockRateUploadInfoRepository.Verify(x => x.RemoveAsync(It.IsAny()), Times.Never); + } + +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/ValidateLoanRateServicesTests/ValidateRateEvaluationsServiceTest.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/ValidateLoanRateServicesTests/ValidateRateEvaluationsServiceTest.cs new file mode 100644 index 0000000..08479bb --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/ServiceTests/ValidateLoanRateServicesTests/ValidateRateEvaluationsServiceTest.cs @@ -0,0 +1,431 @@ +using LCU.API.Internal.LoanRate.Services; +using LCU.API.Internal.LoanRate.Services.LoanRateOperationServices; +using LCU.API.Internal.LoanRate.Services.ValidateLoanRateServices; +using LCU.Library.Internal.LcuEvaluationModels.Entities; +using LCU.Library.Internal.LcuEvaluationModels.Enums; +using LCU.Library.Internal.LcuEvaluation.Services; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.Library.Internal.LoanRateModels.Requests; +using LCU.Library.Internal.LcuEvaluation.Services.EvaluationServices; + +namespace LCU.API.Internal.LoanRateTests.ServiceTests.ValidateLoanRateServicesTests; + +public class ValidateRateEvaluationsServiceTest +{ + private readonly ValidateRateEvaluationsService _validateRateEvaluationsService; + private readonly Mock _mockEvaluationDetailService; + private readonly Mock _mockRateService; + private readonly Mock _mockRunEvaluationService; + private readonly Mock _mockRateFinderService; + + public ValidateRateEvaluationsServiceTest() + { + _mockEvaluationDetailService = new Mock(); + _mockRateService = new Mock(); + _mockRunEvaluationService = new Mock(); + _mockRateFinderService = new Mock(); + _validateRateEvaluationsService = new ValidateRateEvaluationsService(_mockEvaluationDetailService.Object, + _mockRateService.Object, + _mockRunEvaluationService.Object, + _mockRateFinderService.Object); + } + + [Fact] + public async Task ValidateRatePassesEvaluation_True() + { + var rateRequest = new RateRequest(); + + var evaluationBuilder1 = new EvaluationDetail() + { + EvaluationDetailId = 1, + Description = "CreditScore Greater Than Or Equal 740.", + EvaluationParameters = new List() + { + new (0, ParameterTypes.Variable, "RateRequest.CreditScore"), + new (1, ParameterTypes.GreaterThanOrEqual, ""), + new (2, ParameterTypes.TextInput, "740") + } + }; + + _mockEvaluationDetailService.Setup(x => x.GetByIdAsync(1)).ReturnsAsync(evaluationBuilder1).Verifiable(); + + var evaluationsReturned = new List + { + new EvaluationDetail + { + EvaluationDetailId = 1 + }, + new EvaluationDetail + { + EvaluationDetailId = 2 + } + }; + _mockRunEvaluationService.Setup(x => x.ValidateRequestRules(rateRequest, It.IsAny>())).Returns(evaluationsReturned).Verifiable(); + + var rate1 = new Rate() + { + RateId = 1, + Value = 5.49M, + RateEvaluations = new List() + { + new RateEvaluation() + { + RateEvaluationId = 1, + RateId = 1, + EvaluationDetailId = 1, + }, + new RateEvaluation() + { + RateEvaluationId = 2, + RateId = 1, + EvaluationDetailId = 2, + } + } + }; + _mockRateService.Setup(x => x.GetByIdAsync(1)).ReturnsAsync(rate1).Verifiable(); + + var result = await _validateRateEvaluationsService.ValidateRatePassesEvaluation(rateRequest, 1, 1); + Assert.True(result); + + _mockEvaluationDetailService.Verify(x => x.GetByIdAsync(1), Times.Once); + _mockRunEvaluationService.Verify(x => x.ValidateRequestRules(It.IsAny(), It.IsAny>()), Times.Once); + _mockRateService.Verify(x => x.GetByIdAsync(1), Times.Once); + } + + [Fact] + public async Task ValidateRatePassesEvaluation_False() + { + var rateRequest = new RateRequest(); + + var evaluationBuilder1 = new EvaluationDetail() + { + EvaluationDetailId = 1, + Description = "CreditScore Greater Than Or Equal 740.", + EvaluationParameters = new List() + { + new EvaluationParameter(0, ParameterTypes.Variable, "RateRequest.CreditScore"), + new EvaluationParameter(1, ParameterTypes.GreaterThanOrEqual, ""), + new EvaluationParameter(2, ParameterTypes.TextInput, "740") + } + }; + + _mockEvaluationDetailService.Setup(x => x.GetByIdAsync(1)).ReturnsAsync(evaluationBuilder1).Verifiable(); + + var evaluationsReturned = new List(); + _mockRunEvaluationService.Setup(x => x.ValidateRequestRules(rateRequest, It.IsAny>())).Returns(evaluationsReturned).Verifiable(); + + var rate1 = new Rate() + { + RateId = 3, + Value = 5.49M, + RateEvaluations = new List() + { + new RateEvaluation() + { + RateEvaluationId = 1, + RateId = 1, + EvaluationDetailId = 1, + }, + new RateEvaluation() + { + RateEvaluationId = 2, + RateId = 1, + EvaluationDetailId = 3, + } + } + }; + _mockRateService.Setup(x => x.GetByIdAsync(1)).ReturnsAsync(rate1).Verifiable(); + + var result = await _validateRateEvaluationsService.ValidateRatePassesEvaluation(rateRequest, 1, 1); + Assert.False(result); + + _mockEvaluationDetailService.Verify(x => x.GetByIdAsync(1), Times.Once); + _mockRunEvaluationService.Verify(x => x.ValidateRequestRules(It.IsAny(), It.IsAny>()), Times.Once); + _mockRateService.Verify(x => x.GetByIdAsync(1), Times.Once); + } + + [Fact] + public async Task ValidateRatePassesEvaluations_True() + { + var rate1 = new Rate() + { + RateId = 3, + Value = 5.49M, + RateEvaluations = new List() + { + new RateEvaluation() + { + RateEvaluationId = 1, + RateId = 1, + EvaluationDetailId = 3, + }, + new RateEvaluation() + { + RateEvaluationId = 2, + RateId = 1, + EvaluationDetailId = 4, + } + } + }; + var applicationDate = DateTime.Now; + + _mockRateService.Setup(x => x.GetByIdAsync(1)).ReturnsAsync(rate1).Verifiable(); + + var evaluationIds = rate1.RateEvaluations.Select(rateEvaluation => rateEvaluation.EvaluationDetailId).ToList(); + _mockEvaluationDetailService.Setup(x => x.GetActiveByEvaluationTypeAndIdsAsync(applicationDate, EvaluationTypes.Rule, evaluationIds)).ReturnsAsync(GetFakeEvaluations()).Verifiable(); + + var evaluationsReturned = new List + { + new EvaluationDetail + { + EvaluationDetailId = 1 + }, + new EvaluationDetail + { + EvaluationDetailId = 2 + } + }; + + var rateRequest = new RateRequest { ApplicationDate = applicationDate }; + _mockRunEvaluationService.Setup(x => x.ValidateRequestRules(rateRequest, It.IsAny>())).Returns(evaluationsReturned).Verifiable(); + + var rateReturn = ( new Rate { RateId = 1 }, string.Empty ); + _mockRateFinderService.Setup(x => x.DetermineRate(applicationDate, evaluationsReturned)).ReturnsAsync(rateReturn).Verifiable(); + + var result = await _validateRateEvaluationsService.ValidateRatePassesEvaluations(rateRequest, 1); + Assert.True(result); + + _mockRateService.Verify(x => x.GetByIdAsync(1), Times.Once); + _mockEvaluationDetailService.Verify(x => x.GetActiveByEvaluationTypeAndIdsAsync(It.IsAny(), It.IsAny(), It.IsAny>()), Times.Once); + _mockRunEvaluationService.Verify(x => x.ValidateRequestRules(It.IsAny(), It.IsAny>()), Times.Once); + _mockRateFinderService.Verify(x => x.DetermineRate(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Fact] + public async Task ValidateRatePassesEvaluations_False() + { + var applicationDate = DateTime.Now; + var rateRequest = new RateRequest() { ApplicationDate = applicationDate }; + + var rate1 = new Rate() + { + RateId = 3, + Value = 5.49M, + RateStartDate = applicationDate.AddDays(-50), + RateEvaluations = new List() + { + new RateEvaluation() + { + RateEvaluationId = 1, + RateId = 1, + EvaluationDetailId = 3, + }, + new RateEvaluation() + { + RateEvaluationId = 2, + RateId = 1, + EvaluationDetailId = 4, + } + } + }; + _mockRateService.Setup(x => x.GetByIdAsync(1)).ReturnsAsync(rate1).Verifiable(); + + + var evaluationIds = rate1.RateEvaluations.Select(rateEvaluation => rateEvaluation.EvaluationDetailId).ToList(); + _mockEvaluationDetailService.Setup(x => x.GetActiveByEvaluationTypeAndIdsAsync(rateRequest.ApplicationDate, EvaluationTypes.Rule, evaluationIds)).ReturnsAsync(GetFakeEvaluations()).Verifiable(); + + var evaluationsReturned = new List + { + new EvaluationDetail + { + EvaluationDetailId = 1, + StartDate = applicationDate.AddDays(-10) + }, + new EvaluationDetail + { + EvaluationDetailId = 2, + StartDate = applicationDate.AddDays(-10) + } + }; + + _mockRunEvaluationService.Setup(x => x.ValidateRequestRules(rateRequest, It.IsAny>())).Returns(evaluationsReturned).Verifiable(); + + var rateReturn = ( new Rate { RateId = 100 }, string.Empty ); + _mockRateFinderService.Setup(x => x.DetermineRate(applicationDate, evaluationsReturned)).ReturnsAsync(rateReturn).Verifiable(); + + var result = await _validateRateEvaluationsService.ValidateRatePassesEvaluations(rateRequest, 1); + Assert.False(result); + + _mockRateService.Verify(x => x.GetByIdAsync(1), Times.Once); + _mockEvaluationDetailService.Verify(x => x.GetActiveByEvaluationTypeAndIdsAsync(It.IsAny(), It.IsAny(), It.IsAny>()), Times.Once); + _mockRunEvaluationService.Verify(x => x.ValidateRequestRules(It.IsAny(), It.IsAny>()), Times.Once); + _mockRateFinderService.Verify(x => x.DetermineRate(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Fact] + public async Task ValidateRatePassesEvaluations_False_NoEvaluations() + { + var rate1 = new Rate() + { + RateId = 3, + Value = 5.49M, + RateEvaluations = new List() + { + new RateEvaluation() + { + RateEvaluationId = 1, + RateId = 1, + EvaluationDetailId = 3, + }, + new RateEvaluation() + { + RateEvaluationId = 2, + RateId = 1, + EvaluationDetailId = 4, + } + } + }; + _mockRateService.Setup(x => x.GetByIdAsync(1)).ReturnsAsync(rate1).Verifiable(); + + _mockEvaluationDetailService.Setup(x => x.GetActiveByEvaluationTypeAndIdsAsync(It.IsAny(), EvaluationTypes.Rule, It.IsAny>())).ReturnsAsync(new List()).Verifiable(); + + var rateRequest = new RateRequest(); + var result = await _validateRateEvaluationsService.ValidateRatePassesEvaluations(rateRequest, 1); + Assert.False(result); + + _mockRateService.Verify(x => x.GetByIdAsync(1), Times.Once); + _mockEvaluationDetailService.Verify(x => x.GetActiveByEvaluationTypeAndIdsAsync(It.IsAny(), It.IsAny(), It.IsAny>()), Times.Once); + _mockRunEvaluationService.Verify(x => x.ValidateRequestRules(It.IsAny(), It.IsAny>()), Times.Never); + _mockRateFinderService.Verify(x => x.DetermineRate(It.IsAny(), It.IsAny>()), Times.Never); + } + + public static List GetFakeEvaluations() + { + var date = DateTime.Now; + var evaluationBuilder1 = new EvaluationDetail() + { + EvaluationDetailId = 1, + Description = "CreditScore Greater Than Or Equal 740.", + StartDate = DateTime.Now.AddDays(-10), + EvaluationParameters = new List() + { + new EvaluationParameter(0, ParameterTypes.Variable, "RateRequest.CreditScore"), + new EvaluationParameter(1, ParameterTypes.GreaterThanOrEqual, ""), + new EvaluationParameter(2, ParameterTypes.TextInput, "740") + } + }; + + var evaluationBuilder2 = new EvaluationDetail() + { + EvaluationDetailId = 2, + Description = "Model Year Greater than or Equal 2022.", + StartDate = DateTime.Now.AddDays(-20), + EvaluationParameters = new List() + { + new EvaluationParameter(0, ParameterTypes.Variable, "RateRequest.ModelYear"), + new EvaluationParameter(1, ParameterTypes.GreaterThanOrEqual, ""), + new EvaluationParameter(2, ParameterTypes.TextInput, "2022") + } + }; + + var evaluationBuilder3 = new EvaluationDetail() + { + EvaluationDetailId = 3, + Description = "Model Year Equal 2021.", + StartDate = DateTime.Now.AddDays(-10), + EvaluationParameters = new List() + { + new EvaluationParameter(0, ParameterTypes.Variable, "RateRequest.ModelYear"), + new EvaluationParameter(1, ParameterTypes.Equal, ""), + new EvaluationParameter(2, ParameterTypes.TextInput, "2021") + } + }; + + var evaluationBuilder4 = new EvaluationDetail() + { + EvaluationDetailId = 4, + StartDate = DateTime.Now.AddDays(-15), + Description = "CreditScore Greater Than Or Equal 700 AND Less Than Or Equal 739.", + EvaluationParameters = new List() + { + new EvaluationParameter(0, ParameterTypes.ParenthesesStart, ""), + new EvaluationParameter(1, ParameterTypes.Variable, "RateRequest.CreditScore"), + new EvaluationParameter(2, ParameterTypes.GreaterThanOrEqual, ""), + new EvaluationParameter(3, ParameterTypes.TextInput, "700"), + new EvaluationParameter(4, ParameterTypes.ParenthesesEnd, ""), + new EvaluationParameter(5, ParameterTypes.And, ""), + new EvaluationParameter(6, ParameterTypes.ParenthesesStart, ""), + new EvaluationParameter(7, ParameterTypes.Variable, "RateRequest.CreditScore"), + new EvaluationParameter(8, ParameterTypes.LessThanOrEqual, ""), + new EvaluationParameter(9, ParameterTypes.TextInput, "739"), + new EvaluationParameter(10, ParameterTypes.ParenthesesEnd, "") + } + }; + + var evaluations = new List() { evaluationBuilder1, evaluationBuilder2, evaluationBuilder3, evaluationBuilder4 }; + return evaluations; + } + + public static List GetFakeRates() + { + var rate1 = new Rate() + { + RateId = 1, + Value = 5.49M, + RateEvaluations = new List() + { + new RateEvaluation() + { + RateEvaluationId = 1, + RateId = 1, + EvaluationDetailId = 1, + }, + new RateEvaluation() + { + RateEvaluationId = 2, + RateId = 1, + EvaluationDetailId = 2, + } + } + }; + + var rate2 = new Rate() + { + RateId = 2, + Value = 5.50M, + RateEvaluations = new List() + { + new RateEvaluation() + { + RateEvaluationId = 3, + RateId = 2, + EvaluationDetailId = 1, + }, + new RateEvaluation() + { + RateEvaluationId = 4, + RateId = 2, + EvaluationDetailId = 3, + } + } + }; + + var rate3 = new Rate() + { + RateId = 3, + Value = 5.51M, + RateEvaluations = new List() + { + new RateEvaluation() + { + RateEvaluationId = 5, + RateId = 3, + EvaluationDetailId = 4, + } + } + }; + + var rates = new List() { rate1, rate2, rate3 }; + return rates; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/Usings.cs b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/Usings.cs new file mode 100644 index 0000000..7a8920d --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.API.Internal.LoanRateTests/Usings.cs @@ -0,0 +1,2 @@ +global using Xunit; +global using Moq; \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Constants/RateRequestVariablesConsts.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Constants/RateRequestVariablesConsts.cs new file mode 100644 index 0000000..01e25fe --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Constants/RateRequestVariablesConsts.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +namespace LCU.Library.Internal.LoanRateModels.Constants; + +public static class RateRequestVariablesConsts +{ + public static readonly List RateRequestVariablesConstValues = new() + { + //RateRequest + RateRequestQualityFactor, + RateRequestTerm, + RateRequestLoanToValue, + RateRequestYear, + RateRequestRateType, + RateRequestApplicationDate, + RateRequestTotalCollateralValue, + + //CalculationRequest + RateCalculationRequestCurrentRate + }; + + //RateRequest + public const string RateRequestQualityFactor = "RateRequest.QualityFactor"; + public const string RateRequestTerm = "RateRequest.Term"; + public const string RateRequestLoanToValue = "RateRequest.LoanToValue"; + public const string RateRequestYear = "RateRequest.Year"; + public const string RateRequestRateType = "RateRequest.RateType"; + public const string RateRequestApplicationDate = "RateRequest.ApplicationDate"; + public const string RateRequestTotalCollateralValue = "RateRequest.TotalCollateralValue"; + + //CalculationRequest + public const string RateCalculationRequestCurrentRate = "RateCalculationRequest.CurrentRate"; +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/LoadTestRunInfo.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/LoadTestRunInfo.cs new file mode 100644 index 0000000..94fc955 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/LoadTestRunInfo.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using LCU.Library.Internal.LoanRateModels.Enums; +namespace LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; + +public class LoadTestRunInfo +{ + public int LoadTestRunInfoId { get; set; } + public DateTime StartTimeStamp { get; set; } + public DateTime? EndTimeStamp { get; set; } = null; + public RateTypes RateType { get; set; } + public int RateProcessingInfosCount { get; set; } + public int RateProcessingInfosCompleteCount { get; set; } = 0; + + [ForeignKey("LoadTestRunInfoId")] + public ICollection RateProcessingInfos { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/LoadTestRunInfoFilter.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/LoadTestRunInfoFilter.cs new file mode 100644 index 0000000..cc5634e --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/LoadTestRunInfoFilter.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using LCU.Library.Internal.LCUFilterModels.Pagination; +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +public class LoadTestRunInfoFilter : Filter +{ + public LoadTestRunInfoFilter() + { + SortableFields = + new() { + new SortableField + { + Field = nameof(LoadTestRunInfo.RateType), + DisplayValue = "Rate Type", + Active = false + }, + new SortableField + { + Field = nameof(LoadTestRunInfo.StartTimeStamp), + DisplayValue = "Start Time", + IsDescending = true, + Active = true + }, + new SortableField + { + Field = nameof(LoadTestRunInfo.EndTimeStamp), + DisplayValue = "End Time", + Active = false + }, + }; + } + + public bool InProgress { get; set; } + public List RateTypes { get; set; } = new(); + public DateTime? StartTimeStamp { get; set; } + public DateTime? EndTimeStamp { get; set; } +} diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/RateProcessingInfo.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/RateProcessingInfo.cs new file mode 100644 index 0000000..ae16d11 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/RateProcessingInfo.cs @@ -0,0 +1,15 @@ +using LCU.Library.Internal.LoanRateModels.Enums.LoadTestingEnums; +namespace LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; + +public class RateProcessingInfo +{ + public int RateProcessingInfoId { get; set; } + public int LoadTestRunInfoId { get; set; } + public string Payload { get; set; } + public decimal Rate { get; set; } + public string Column { get; set; } + public string Row { get; set; } + public RateProcessingStatusTypes RateProcessingStatusType { get; set; } + public RateProcessingResultTypes RateProcessingResultType { get; set; } + public string ProcessingMessage { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/RateProcessingInfoFilter.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/RateProcessingInfoFilter.cs new file mode 100644 index 0000000..6d79c8e --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/LoadTestingEntities/RateProcessingInfoFilter.cs @@ -0,0 +1,6 @@ +using LCU.Library.Internal.LCUFilterModels.Pagination; + +namespace LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +public class RateProcessingInfoFilter : Filter +{ +} diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/Rate.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/Rate.cs new file mode 100644 index 0000000..d5bf1e7 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/Rate.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace LCU.Library.Internal.LoanRateModels.Entities; + +public class Rate +{ + public int RateId { get; set; } + public int? RateUploadInfoId { get; set; } + public int? ClosingRateUploadInfoId { get; set; } + public string Column { get; set; } + public string Row { get; set; } + public int RateSheetId { get; set; } + public decimal Value { get; set; } + [Required] + [Range(typeof(DateTime), "1/2/0001", "12/30/9999", ErrorMessage = "Value for {0} must be between {1} and {2}")] + public DateTime RateStartDate { get; set; } + public DateTime? RateEndDate { get; set; } + + [ForeignKey("RateId")] + public ICollection RateEvaluations { get; set; } + + public Rate DeepCopyNewRate() + { + Rate newRate = (Rate)this.MemberwiseClone(); + newRate.RateId = 0; + newRate.RateEvaluations = new List(); + foreach (var rateRule in RateEvaluations) + { + var newRateRule = (RateEvaluation)rateRule.ShallowCopyNewRate(); + newRate.RateEvaluations.Add(newRateRule); + } + + return newRate; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateEvaluation.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateEvaluation.cs new file mode 100644 index 0000000..96163ac --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateEvaluation.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations.Schema; +using LCU.Library.Internal.LcuEvaluationModels.Entities; +namespace LCU.Library.Internal.LoanRateModels.Entities; + +public class RateEvaluation +{ + public int RateEvaluationId { get; set; } + public int RateId { get; set; } + public int EvaluationDetailId { get; set; } + + [ForeignKey("EvaluationDetailId")] + public EvaluationDetail EvaluationDetail { get; set; } + + public RateEvaluation ShallowCopyNewRate() + { + var newRateRule = (RateEvaluation)MemberwiseClone(); + newRateRule.RateEvaluationId = 0; + newRateRule.RateId = 0; + return newRateRule; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateSheet.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateSheet.cs new file mode 100644 index 0000000..1de6064 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateSheet.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using LCU.Library.Internal.LoanRateModels.Enums; +using LCU.Library.Internal.TypeExtensions.EnumExtensions; + +namespace LCU.Library.Internal.LoanRateModels.Entities; + +public class RateSheet +{ + [Key] + public int RateSheetId { get; set; } + [Required] + public RateTypes RateType { get; set; } + [Required] + public string CreatedBy { get; set; } = "System"; + public DateTime TimeCreated { get; set; } = DateTime.Now; + + public string SheetName => RateType.GetNameFromValue(); + + [ForeignKey("RateSheetId")] + public ICollection Rates { get; set; } + + [ForeignKey("RateSheetId")] + public ICollection RateSheetCells { get; set; } + +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateSheetCell.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateSheetCell.cs new file mode 100644 index 0000000..d690d19 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateSheetCell.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.Library.Internal.LoanRateModels.Entities; + +public class RateSheetCell +{ + [Key] + public int RateSheetCellId { get; set; } + [Required] + public int RateSheetId { get; set; } + [Required] + public string Column { get; set; } + public int ColumnSpan { get; set; } = 1; + [Required] + public string Row { get; set; } + public int RowSpan { get; set; } = 1; + public string Value { get; set; } + public RateSheetCellTypes RateSheetCellType { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateUploadInfo.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateUploadInfo.cs new file mode 100644 index 0000000..a4ba410 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateUploadInfo.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LCU.Library.Internal.LoanRateModels.Entities; + +public class RateUploadInfo +{ + [Key] + public int RateUploadInfoId { get; set; } + public int Version { get; set; } + public string Filename { get; set; } + public DateTime EffectiveDate { get; set; } + public DateTime TimeCreated { get; set; } + public string CreatedBy { get; set; } + public int RateCount { get; set; } + + [ForeignKey("RateUploadInfoId")] + public ICollection Rates { get; set; } + + [ForeignKey("ClosingRateUploadInfoId")] + public ICollection? ClosedRates { get; set; } +} diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateUploadInfoFilter.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateUploadInfoFilter.cs new file mode 100644 index 0000000..88b80d2 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Entities/RateUploadInfoFilter.cs @@ -0,0 +1,33 @@ +using System; +using LCU.Library.Internal.LCUFilterModels.Pagination; + +namespace LCU.Library.Internal.LoanRateModels.Entities; +public class RateUploadInfoFilter : Filter +{ + public RateUploadInfoFilter() + { + SortableFields = + new() { + new SortableField + { + Position = 0, + Field = nameof(RateUploadInfo.EffectiveDate), + DisplayValue = "Effective Date", + IsDescending = true, + Active = true + }, + new SortableField + { + Position = 1, + Field = nameof(RateUploadInfo.Version), + DisplayValue = "Version", + IsDescending = true, + Active = true + } + }; + } + + public int? Version { get; set; } + public DateTime? EffectiveDate { get; set; } + public string? CreatedBy { get; set; } +} diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/LoadTestingEnums/LoadTestingRateStatustype.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/LoadTestingEnums/LoadTestingRateStatustype.cs new file mode 100644 index 0000000..619f24c --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/LoadTestingEnums/LoadTestingRateStatustype.cs @@ -0,0 +1,8 @@ +namespace LCU.Library.Internal.LoanRateModels.Enums.LoadTestingEnums; + +public enum RateProcessingResultTypes +{ + NA = 0, + RateFound = 1, + RateFailure = 2 +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/LoadTestingEnums/RateProcessingStatusTypes.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/LoadTestingEnums/RateProcessingStatusTypes.cs new file mode 100644 index 0000000..250d4e7 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/LoadTestingEnums/RateProcessingStatusTypes.cs @@ -0,0 +1,8 @@ +namespace LCU.Library.Internal.LoanRateModels.Enums.LoadTestingEnums; + +public enum RateProcessingStatusTypes +{ + Processing = 0, + Complete = 1, + CriticalFailure = 2 +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/RateSheetCellTypes.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/RateSheetCellTypes.cs new file mode 100644 index 0000000..a62cff9 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/RateSheetCellTypes.cs @@ -0,0 +1,7 @@ +namespace LCU.Library.Internal.LoanRateModels.Enums; + +public enum RateSheetCellTypes +{ + TopHeader, + LeftHeader +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/RateTypes.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/RateTypes.cs new file mode 100644 index 0000000..23dd30c --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Enums/RateTypes.cs @@ -0,0 +1,34 @@ +using System.ComponentModel.DataAnnotations; +namespace LCU.Library.Internal.LoanRateModels.Enums; + +public enum RateTypes +{ + [Display(Name = "Direct Boat Rates")] + DirectBoat = 0, + [Display(Name = "Direct Powersport Rates")] + DirectPowerSport = 1, + [Display(Name = "Direct RV Rates")] + DirectRv = 2, + [Display(Name = "Direct Signature Loan Rates")] + DirectSignatureLoan = 3, + [Display(Name = "Direct Motorcycle Rates")] + DirectMotorcycle = 4, + [Display(Name = "Direct Auto Rates")] + DirectAuto = 5, + [Display(Name = "WI Indirect Boat Buy Rates")] + WiIndirectBoatBuy = 6, + [Display(Name = "IL Indirect Boat Buy Rates")] + IlIndirectBoatBuy = 7, + [Display(Name = "WI Indirect Motorcycle Buy Rate")] + WiIndirectMotorcycleBuy = 8, + [Display(Name = "WI Indirect Powersport Buy Rate")] + WiIndirectPowerSportBuy = 9, + [Display(Name = "IL Indirect Motorcycle Buy Rate")] + IlIndirectMotorcycleBuy = 10, + [Display(Name = "IL Indirect Powersport Buy Rate")] + IlIndirectPowerSportBuy = 11, + [Display(Name = "WI Indirect Auto Buy Rates")] + WiIndirectAutoBuy = 12, + [Display(Name = "IL Indirect Auto Buy Rates")] + IlIndirectAutoBuy = 13 +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/LCU.Library.Internal.LoanRateModels.csproj b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/LCU.Library.Internal.LoanRateModels.csproj new file mode 100644 index 0000000..1ca34d8 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/LCU.Library.Internal.LoanRateModels.csproj @@ -0,0 +1,83 @@ + + + + net8.0 + True + latest + 2.4.0 + + 1.0.0 - Initial Creation of Models Library. + 1.1.0 - Refactor to use Evaluation, Added RateRequestVariablesConsts, and added RateCalcuationRequest + 1.2.0 - Fix RateRequestVariablesConst's namespace and added static to class/list + 1.2.2 - Update LoanRateEvaluationModels library + 1.2.3 - Added to VehicleRateRequest, created VehiclePurposeTypes, created VehicleRateTypes, and added to RateRequestVariablesConsts. + 1.2.4 - Fix NonCashOutRefis typo. + 1.3.0 - Adding RateRequest which has the RateType property and also extends EvaluationRequest. VehicleRateRequest now extends RateRequest. + 1.4.0 - Adding VehicleRateRequest VehicleTypes and removed the LTV Calc as it will be entered. + 1.4.1 - Rename new enum VehicleTypes to VehicleSubTypes and add default of none. + 1.4.2 - Update LoanRateEvaluationModels library + 1.5.0 - Adding HomeEquityRateRequest, HomeLoanTypes, and LienPositionTypes + 1.6.0 - Updating LoanRateEvaluationModels for CurrentYear Parameter + 2.0.0 - Refactor RateRequest and RateTypes, add RateResponse + 2.1.0 - Add InitiateRatesEvaluationsResponse model + 2.2.0 - Add RateUploadExcelDataRequest and RateUploadRequest models + 2.2.1 - Fix RateRequestVariablesConsts RateRequest.Year + 2.3.0 - Adding LoadTesting Entities and Request models. + 2.3.1 - Update LoanRateEvaluationModels library + 2.3.2 - Ratetype change DirectMrex to be DirectSignatureLoan. + 2.3.3 - Add LoadTestRunInfoFilter + 2.3.4 - Add RateProcessingInfoFilter + 2.3.5 - Add RateUploadInfo + 2.3.6 - Change Rate.RateUploadInfoId to nullable + 2.3.7 - Add RateUploadInfoFilter + 2.3.8 - Update RateUploadInfo.Filepath to RateUploadInfo.Filename + 2.3.9 - Upgrade to .Net 8 + 2.3.10 - Add ClosedRates to RateUploadInfo + 2.3.11 - Add RateSheetCellTypes enum + 2.3.12 - Add RateUploadInfoRequest + 2.4.0 - Add Validation models + + + 1.0.0 - Initial Creation of Models Library. + 1.1.0 - Refactor to use Evaluation, Added RateRequestVariablesConsts, and added RateCalcuationRequest + 1.2.0 - Fix RateRequestVariablesConst's namespace and added static to class/list + 1.2.1 - Update LoanRateEvaluationModels library + 1.2.2 - Update LoanRateEvaluationModels library + 1.2.3 - Added to VehicleRateRequest, created VehiclePurposeTypes, created VehicleRateTypes, and added to RateRequestVariablesConsts. + 1.2.4 - Fix NonCashOutRefis typo. + 1.3.0 - Adding RateRequest which has the RateType property and also extends EvaluationRequest. VehicleRateRequest now extends RateRequest. + 1.4.0 - Adding VehicleRateRequest VehicleTypes and removed the LTV Calc as it will be entered. + 1.4.1 - Rename new enum VehicleTypes to VehicleSubTypes and add default of none. + 1.4.2 - Update LoanRateEvaluationModels library + 1.5.0 - Adding HomeEquityRateRequest, HomeLoanTypes, and LienPositionTypes + 1.6.0 - Updating LoanRateEvaluationModels for CurrentYear Parameter + 2.0.0 - Refactor RateRequest and RateTypes, add RateResponse + 2.1.0 - Add InitiateRatesEvaluationsResponse model + 2.2.0 - Add RateUploadExcelDataRequest and RateUploadRequest models + 2.2.1 - Fix RateRequestVariablesConsts RateRequest.Year + 2.3.0 - Adding LoadTesting Entities and Request models. + 2.3.1 - Update LoanRateEvaluationModels library + 2.3.2 - Ratetype change DirectMrex to be DirectSignatureLoan. + 2.3.3 - Add LoadTestRunInfoFilter + 2.3.4 - Add RateProcessingInfoFilter + 2.3.5 - Add RateUploadInfo + 2.3.6 - Change Rate.RateUploadInfoId to nullable + 2.3.7 - Add RateUploadInfoFilter + 2.3.8 - Update RateUploadInfo.Filepath to RateUploadInfo.Filename + 2.3.9 - Upgrade to .Net 8 + 2.3.10 - Add ClosedRates to RateUploadInfo + 2.3.11 - Add RateSheetCellTypes enum + 2.3.12 - Add RateUploadInfoRequest + 2.4.0 - Add Validation models + + + + + + + + + + + + diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/InitiateRatesEvaluationsRequest.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/InitiateRatesEvaluationsRequest.cs new file mode 100644 index 0000000..4741aaf --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/InitiateRatesEvaluationsRequest.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +namespace LCU.Library.Internal.LoanRateModels.Requests; + +public class InitiateRatesEvaluationsRequest +{ + [Required] + public string SheetName { get; set; } + [Required] + public int EvaluationDetailId { get; set; } + public List RowsOrColumns { get; set; } + public bool IsRowsRule { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/LoadTestRequest/LoadTestInitiateRequest.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/LoadTestRequest/LoadTestInitiateRequest.cs new file mode 100644 index 0000000..30f09e9 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/LoadTestRequest/LoadTestInitiateRequest.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using LCU.Library.Internal.LoanRateModels.Enums; +namespace LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; + +public class LoadTestInitiateRequest +{ + [Required] + public RateTypes? RateType { get; set; } + [PropertyListValidation] + public Dictionary> PropertyList { get; set; } +} + +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] +public class PropertyListValidation : ValidationAttribute +{ + protected override ValidationResult IsValid(object value, ValidationContext validationContext) + { + if (!(value is Dictionary> dictionary)) + return new ValidationResult("Object is not of proper type"); + + if (dictionary.ContainsKey(nameof(RateRequest.RateType))) + { + return new ValidationResult($"{nameof(RateRequest.RateType)} cannot be declared in the PropertyList."); + } + + return ValidationResult.Success; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/LoadTestRequest/LoadTestProcessRateRequest.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/LoadTestRequest/LoadTestProcessRateRequest.cs new file mode 100644 index 0000000..1646972 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/LoadTestRequest/LoadTestProcessRateRequest.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +namespace LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; + +public class LoadTestProcessRateRequest +{ + public RateProcessingInfo RateProcessingInfo { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/LoadTestRequest/LoadTestRateCompletionRequest.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/LoadTestRequest/LoadTestRateCompletionRequest.cs new file mode 100644 index 0000000..7e3b3b9 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/LoadTestRequest/LoadTestRateCompletionRequest.cs @@ -0,0 +1,7 @@ +using LCU.Library.Internal.LoanRateModels.Entities.LoadTestingEntities; +namespace LCU.Library.Internal.LoanRateModels.Requests.LoadTestRequest; + +public class LoadTestRateCompletionRequest +{ + public RateProcessingInfo RateProcessingInfo { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateCalculationRequest.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateCalculationRequest.cs new file mode 100644 index 0000000..e6c406b --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateCalculationRequest.cs @@ -0,0 +1,9 @@ +using System.ComponentModel; +using LCU.Library.Internal.LcuEvaluationModels.Requests; +namespace LCU.Library.Internal.LoanRateModels.Requests; + +public class RateCalculationRequest : EvaluationRequest +{ + [DisplayName("RateCalculationRequest.CurrentRate")] + public decimal CurrentRate { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateRequest.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateRequest.cs new file mode 100644 index 0000000..0989bc0 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateRequest.cs @@ -0,0 +1,29 @@ +using System; +using System.ComponentModel; +using LCU.Library.Internal.LcuEvaluationModels.Requests; +using LCU.Library.Internal.LoanRateModels.Enums; +namespace LCU.Library.Internal.LoanRateModels.Requests; + +public class RateRequest : EvaluationRequest +{ + [DisplayName("RateRequest.QualityFactor")] + public string QualityFactor { get; set; } + + [DisplayName("RateRequest.Term")] + public int Term { get; set; } + + [DisplayName("RateRequest.LoanToValue")] + public decimal LoanToValue { get; set; } + + [DisplayName("RateRequest.Year")] + public string Year { get; set; } + + [DisplayName("RateRequest.RateType")] + public RateTypes RateType { get; set; } + + [DisplayName("RateRequest.ApplicationDate")] + public DateTime ApplicationDate { get; set; } + + [DisplayName("RateRequest.TotalCollateralValue")] + public decimal TotalCollateralValue { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateSheetCellUpload.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateSheetCellUpload.cs new file mode 100644 index 0000000..4cea534 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateSheetCellUpload.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.Library.Internal.LoanRateModels.Requests; +public class RateSheetCellUpload +{ + public RateTypes RateType { get; set; } + public List HeaderTopColumns { get; set; } + public List HeaderTopRows { get; set; } + public List HeaderLeftColumns { get; set; } + public List HeaderLeftRows { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateSheetCellUploadRequest.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateSheetCellUploadRequest.cs new file mode 100644 index 0000000..de5ab2f --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateSheetCellUploadRequest.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace LCU.Library.Internal.LoanRateModels.Requests; +public class RateSheetCellUploadRequest +{ + public string FileContent { get; set; } + public List Requests { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateSheetRequest.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateSheetRequest.cs new file mode 100644 index 0000000..ada2f67 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateSheetRequest.cs @@ -0,0 +1,9 @@ +using System; +using LCU.Library.Internal.LoanRateModels.Enums; + +namespace LCU.Library.Internal.LoanRateModels.Requests; +public class RateSheetRequest +{ + public RateTypes RateType { get; set; } + public DateTime EffectiveDate { get; set; } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateUploadExcelDataRequest.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateUploadExcelDataRequest.cs new file mode 100644 index 0000000..bcdd75b --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateUploadExcelDataRequest.cs @@ -0,0 +1,11 @@ +using System; +using LCU.Api.Internal.FileOperationsLib.Models.ExcelModels.Requests; + +namespace LCU.Library.Internal.LoanRateModels.Requests; +public class RateUploadExcelDataRequest +{ + public string FileName { get; set; } + public DateTime EffectiveDate { get; set; } + public ExcelDataRequest ExcelDataRequest { get; set; } + public string CreatedBy { get; set; } = "System"; +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateUploadInfoRequest.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateUploadInfoRequest.cs new file mode 100644 index 0000000..aac7dd3 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateUploadInfoRequest.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using LCU.Library.Internal.LoanRateModels.Entities; + +namespace LCU.Library.Internal.LoanRateModels.Requests; +public class RateUploadInfoRequest +{ + public List Rates { get; set; } = new(); + public string FileName { get; set; } + public DateTime EffectiveDate { get; set; } + public string CreatedBy { get; set; } = "System"; +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateUploadRequest.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateUploadRequest.cs new file mode 100644 index 0000000..d5aeea3 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Requests/RateUploadRequest.cs @@ -0,0 +1,10 @@ +using System; + +namespace LCU.Library.Internal.LoanRateModels.Requests; +public class RateUploadRequest +{ + public string FileContent { get; set; } + public string FileName { get; set; } + public DateTime EffectiveDate { get; set; } + public string CreatedBy { get; set; } = "System"; +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/InitiateRatesEvaluationsResponse.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/InitiateRatesEvaluationsResponse.cs new file mode 100644 index 0000000..b19249a --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/InitiateRatesEvaluationsResponse.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using LCU.Library.Internal.LoanRateModels.Entities; +using LCU.Library.Internal.LoanRateModels.Requests; + +namespace LCU.Library.Internal.LoanRateModels.Responses; + +public class InitiateRatesEvaluationsResponse +{ + public bool Success { get; set; } = true; + public InitiateRatesEvaluationsRequest Request { get; set; } + public List RateEvaluationResults { get; set; } = new(); + public List Messages { get; set; } = new(); + +} + +public static class InitiateRatesEvaluationsResponseUtilities +{ + public static InitiateRatesEvaluationsResponse AddSuccessMessage(this InitiateRatesEvaluationsResponse response, string message) + { + response.Success = true; + response.Messages.Add(message); + return response; + } + + public static InitiateRatesEvaluationsResponse AddFailureMessage(this InitiateRatesEvaluationsResponse response, string message) + { + response.Success = false; + response.Messages.Add(message); + return response; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/RateResponse.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/RateResponse.cs new file mode 100644 index 0000000..60f8d2b --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/RateResponse.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; + +namespace LCU.Library.Internal.LoanRateModels.Responses; + +public class RateResponse +{ + public bool Success { get; set; } = true; + public decimal Rate { get; set; } + public List Messages { get; set; } = new(); +} + +public static class RateResponseUtilities +{ + private const decimal RateDefaultValue = (decimal)99.0; + + public static RateResponse AddErrorMessage(this RateResponse rateResponse, string message) + { + rateResponse.Messages.Add(message); + rateResponse.Success = false; + return rateResponse; + } + + public static RateResponse AddErrorMessage(this RateResponse rateResponse, IEnumerable message) + { + rateResponse.Messages.AddRange(message); + return rateResponse; + } + + public static RateResponse SetDefault(this RateResponse rateResponse) + { + return new RateResponse + { + Messages = rateResponse.Messages, + Rate = RateDefaultValue + }; + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/RateUploadInfoFilterResponse.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/RateUploadInfoFilterResponse.cs new file mode 100644 index 0000000..b3dabdc --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/RateUploadInfoFilterResponse.cs @@ -0,0 +1,20 @@ +using LCU.Library.Internal.LCUFilterModels.Pagination; +using LCU.Library.Internal.LoanRateModels.Entities; + +namespace LCU.Library.Internal.LoanRateModels.Responses; + +public class RateUploadInfoFilterResponse : PagedResponse +{ + public RateUploadInfoFilterResponse() { } + + public RateUploadInfoFilterResponse(RateUploadInfoFilterResult pagedResult, RateUploadInfoFilter filter) + { + TotalCount = pagedResult.TotalCount; + LatestRateUploadInfo = pagedResult.LatestRateUploadInfo; + PagedResponse = new PagedResponse(pagedResult, filter); + } + + public int TotalCount { get; set; } + public RateUploadInfo LatestRateUploadInfo { get; set; } + public PagedResponse PagedResponse { get; set; } +} diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/RateUploadInfoFilterResult.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/RateUploadInfoFilterResult.cs new file mode 100644 index 0000000..e2a5e35 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/RateUploadInfoFilterResult.cs @@ -0,0 +1,10 @@ +using LCU.Library.Internal.LCUFilterModels.Pagination; +using LCU.Library.Internal.LoanRateModels.Entities; + +namespace LCU.Library.Internal.LoanRateModels.Responses; + +public class RateUploadInfoFilterResult : PagedResult +{ + public int TotalCount { get; set; } + public RateUploadInfo LatestRateUploadInfo { get; set; } +} diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidation.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidation.cs new file mode 100644 index 0000000..43020b9 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidation.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace LCU.Library.Internal.LoanRateModels.Responses.Validation.RateSheetValidations; +public static class RateSheetValidation +{ + public static RateSheetValidationItem IsWithinTrendRange(decimal oldValue, decimal newValue, List history) + { + var validation = new RateSheetValidationItem + { + Label = "Within Trend Range", + Description = "If there is a history of change, is the current trend within the trend history's max and min values.", + RateSheetValidationType = RateSheetValidationTypes.IsWithinTrendRange + }; + + if (newValue == 0 || oldValue == 0) + { + validation.Pass = false; + validation.Message = $"Values cannot be zero. OldValue: {oldValue} | NewValue: {newValue}"; + return validation; + } + + var currentTrend = oldValue / newValue; + var trends = history.Zip(history.Skip(1), (x, y) => y / x).ToList(); + + var currentTrendString = Math.Round(currentTrend * 100, 2).ToString("0.##"); + var minTrendString = Math.Round(trends.Min() * 100, 2).ToString("0.##"); + var maxTrendString = Math.Round(trends.Max() * 100, 2).ToString("0.##"); + + validation.Value = currentTrendString; + validation.Formula = trends.Count == 0 + ? "No history found" + : $"{currentTrendString} >= {minTrendString} && {currentTrendString} <= {maxTrendString}"; + validation.Criteria = trends.Count == 0 + ? "No history found" + : $"{minTrendString}% - {maxTrendString}%"; + validation.Pass = trends.Count == 0 || currentTrend >= trends.Min() && currentTrend <= trends.Max(); + return validation; + } + + public static RateSheetValidationItem IsWithinRange(decimal value, decimal min, decimal max) + => new() + { + Label = "Within Range", + Description = "Value is within the min and max values.", + Value = value.ToString("0.##"), + Formula = $"{value:0.##} >= {min:0.##} && {value:0.##} <= {max:0.##}", + Criteria = $"{min:0.##} - {max:0.##}", + Pass = value >= min && value <= max, + RateSheetValidationType = RateSheetValidationTypes.IsWithinRange + }; +} diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidationItem.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidationItem.cs new file mode 100644 index 0000000..e8a8823 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidationItem.cs @@ -0,0 +1,5 @@ +namespace LCU.Library.Internal.LoanRateModels.Responses.Validation.RateSheetValidations; +public class RateSheetValidationItem : ValidationItem +{ + public RateSheetValidationTypes RateSheetValidationType { get; set; } +} diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidationResult.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidationResult.cs new file mode 100644 index 0000000..7dcf961 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidationResult.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; +using LCU.Library.Internal.LoanRateModels.Entities; + +namespace LCU.Library.Internal.LoanRateModels.Responses.Validation.RateSheetValidations; +public class RateSheetValidationResult : ValidationResult +{ + public override List Validations { get; set; } +} diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidationTypes.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidationTypes.cs new file mode 100644 index 0000000..cf2cbec --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/RateSheetValidations/RateSheetValidationTypes.cs @@ -0,0 +1,6 @@ +namespace LCU.Library.Internal.LoanRateModels.Responses.Validation.RateSheetValidations; +public enum RateSheetValidationTypes +{ + IsWithinTrendRange, + IsWithinRange +} diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/ValidationItem.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/ValidationItem.cs new file mode 100644 index 0000000..4762b19 --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/ValidationItem.cs @@ -0,0 +1,11 @@ +namespace LCU.Library.Internal.LoanRateModels.Responses.Validation; +public class ValidationItem +{ + public string Label { get; set; } = string.Empty; + public string Value { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string Criteria { get; set; } = string.Empty; + public string Formula { get; set; } = string.Empty; + public string Message { get; set; } = string.Empty; + public bool Pass { get; set; } +} diff --git a/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/ValidationResult.cs b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/ValidationResult.cs new file mode 100644 index 0000000..3fbb5ed --- /dev/null +++ b/LCU.API.Internal.LoanRate/LCU.Library.Internal.LoanRateModels/Responses/Validation/ValidationResult.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace LCU.Library.Internal.LoanRateModels.Responses.Validation; +public class ValidationResult where TValidationItem : ValidationItem +{ + public TEntity NewItem { get; set; } + public TEntity? OriginalItem { get; set; } + public virtual List Validations { get; set; } + public string Message { get; set; } = string.Empty; + public bool Pass { get; set; } = true; + + public List ValidationMessages => Validations.Where(x => !string.IsNullOrWhiteSpace(x.Message)) + .Select(x => x.Message).ToList(); + public List PassedValidations => Validations.Where(x => x.Pass).ToList(); + public List FailedValidations => Validations.Where(x => !x.Pass).ToList(); + public double Percentage => Math.Round(((double)PassedValidations.Count / Validations.Count) * 100, 2); + + public bool CheckValidation() + { + Pass = Validations.Count == 0 || Validations.All(x => x.Pass); + return Pass; + } +} diff --git a/LCU.API.Internal.LoanRate/README.md b/LCU.API.Internal.LoanRate/README.md new file mode 100644 index 0000000..ec10e77 --- /dev/null +++ b/LCU.API.Internal.LoanRate/README.md @@ -0,0 +1,120 @@ +# Introduction + +--- + +The LoanRate API was created to assist in rate creation and determining for loans. The rate requests commonly come from +Temenos, as part of the Consumer LOS project. + + +# Getting Started + +--- + +The required environment is .NET Core 6.0, Git, Docker Desktop, RabbitMQ, and a local FileOperationsApi instance. + +- To run this API you will need a running RabbitMQ on Docker desktop + - Run the following in a cmd + - docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management + - Access rabbitmq management UI http://localhost:15672/# + - UserName: guest, Password: guest +- You will need a local FileOperations running + - FileOperations is a queue based API this application will need to be run locally. + For LoanRate to hear back when this has been performed, it must give the + API a ResponseItem. This uses the localhost rabbitmq set up. This should not use the QA RabbitMQ + as the QA API would then pick up the request and can't communicate to your local rabbitmq. + - FileOperations + - DevOps Repo: https://landmarkcu.visualstudio.com/Platform/_git/LCU.API.Internal.FileOperations + - When a FileOperations request such as GetFileAsync is requested it is placed on a queue. +- If not done already, you will need to publish your local instance of the LoanRate and LoanRateLoadTest databases + +### Suggestions for IDE + +- JetBrains Rider +- Visual Studio 2022 + +### Instances + +Multiple instances have been built to help with running the LoadTest services without effecting the default instance in +production. + +Current instances are: +- Default +- LoadTestInitiate +- LoadTestProcessRate +- LoadTestRateCompletion + +# Testing + +--- + +### Integration Testing + +To test the speed and accuracy of the RateFinderService, a suite of testing tools was developed. + +See [LoadTestServices](#loadtestservices) for details and use. + +### Unit Tests + +Unit tests are available for each service in their own respective test project. + +# Services + +--- + +### RateFinderService + +The RateFinderService used to automatically determine a rate based on the criteria specified in a RateRequest. + +#### Process + +1. LoanRate API retrieves the EvaluationDetails (rules) for the rate type and date. +2. Each rule is built into a temporary logic table, and run against the request. +3. The passing rules are then returned and used to retrieve the matching rates. +4. The matching rates are then built into a temporary logic table to calculate the final rate. + - if no rate is found, a default value is returned. + +### RateUploaderService + +The RateUploaderService is used to upload an Excel sheet from the business through the front-end application, +[LoanRateManager](https://landmarkcu.visualstudio.com/Platform/_git/LCU.WebApps.Internal.LoanRateManager), and transform +the cells into rates to be used in logic for RateFinder. + +#### Process + +1. LoanRate API retrieves all Rates that effect the upload request's effective date. +2. The file is sent to [FileOperations API](https://landmarkcu.visualstudio.com/Platform/_git/LCU.API.Internal.FileOperations) +to be separated, categorized, and sent back. +3. LoanRate API then compares the response's cells with the collected Rates. + - Rates that match the cell and row of a previous rate are updated + - Rates that are duplicates are discarded + - Rates that are new are added + +The act of an "upload" is tied to a RateUploadInfo. From there you can manage these in +[LoanRateManager](https://landmarkcu.visualstudio.com/Platform/_git/LCU.WebApps.Internal.LoanRateManager) to delete and +override previous uploads. + +### LoadTestServices + +The LoadTest projects and services were made to batch test the effectiveness of the current logic and uploaded rates. + +#### Services + +- LoadTestInitiationService +- LoadTestProcessRateService +- LoadTestRateCompletionService + +#### Process + +1. The LoadTestInitiateRequest takes the lists from the request and creates every permutation possible for those lists. +2. The LoadTestInitiationService builds a RateRequest for each permutation, and queues it to the LoadTestProcessRateService. +3. The LoadTestProcessRateService runs each request against the RateFinderService and stores the result. +4. The LoadTestRateCompletionService processes the completed result and adds it to the LoadTestRunInfo to store in the database. + +The results to these tests can be reviewed at [LoanRateManager](https://landmarkcu.visualstudio.com/Platform/_git/LCU.WebApps.Internal.LoanRateManager). + +# Helpful Documents + +--- + +- [LoanRate Workflow Design](https://landmarkcu.sharepoint.com/:u:/r/sites/ITProgrammingDevelopmentTeam/_layouts/15/Doc.aspx?sourcedoc=%7B1F0DB00D-0086-42DC-BA31-B8B781003F60%7D&file=LoanRate_Workflow.vsdx&action=default&mobileredirect=true) +- [LoanRate LoadTest Workflow Design](https://landmarkcu.sharepoint.com/:u:/r/sites/ITProgrammingDevelopmentTeam/_layouts/15/Doc.aspx?sourcedoc=%7BDBA7674E-35CB-4D34-A982-F837BFBB35E9%7D&file=Loan%20Rate%20-%20Mass%20Testing%20WorkFlow%20Diagram.vsdx&action=default&mobileredirect=true) \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/azdo_pipelines/azure-k8s-pipeline.yaml b/LCU.API.Internal.LoanRate/azdo_pipelines/azure-k8s-pipeline.yaml new file mode 100644 index 0000000..9e8a7b0 --- /dev/null +++ b/LCU.API.Internal.LoanRate/azdo_pipelines/azure-k8s-pipeline.yaml @@ -0,0 +1,220 @@ +parameters: + - name: target_environment + displayName: Target Environment + type: string + default: qa + values: + - qa + - uat + - prd + - name: k8s_namespace + displayName: K8s Namespace to deploy to + type: string + default: landmarkcu + values: + - landmarkcu +variables: + - template: ./config-files/${{ parameters.target_environment }}-pvars.yaml + - name: source_branch_name + value: $[replace(replace(variables['Build.SourceBranch'], 'refs/heads/', ''), '/', '-')] + - name: docker_container_repository_name + value: landmarkcu/lcu.api.internal.loanrate-loanrate + - name: buildConfiguration + ${{ if or(eq(variables['Build.SourceBranchName'], 'main'), eq(variables['Build.SourceBranchName'], 'develop')) }}: + value: Release + ${{ else }}: + value: Debug + +resources: + repositories: + - repository: azdo_pipeline_templates + type: git + name: Infrastructure Platform/azdo-pipeline-templates + ref: main + +stages: + + # Restore/Build NuGet packages and push evaluation dacpac artifact to staging directory + - stage: dotnet_nuget + displayName: Restore / Build / Upload Nuget packages + jobs: + - job: build_and_push + displayName: Dotnet Nuget + pool: + name: $(pvar_azdo_agent_pool_nuget) + demands: + - msbuild + steps: + - task: DotNetCoreCLI@2 + displayName: Nuget Restore + inputs: + command: 'restore' + feedsToUse: 'select' + projects: '**/*.csproj' + vstsFeed: 'LCU-Internal' + + - task: DotNetCoreCLI@2 + displayName: Nuget Build + inputs: + command: 'build' + projects: '**/*.csproj' + arguments: '--configuration ${{ variables.buildConfiguration }}' + + - publish: './LCU.API.Internal.LoanRate/bin/${{ variables.buildConfiguration }}/LCU.Library.Internal.LcuEvaluationSql.dacpac' + artifact: Evaluation_Dacpac + + - task: DotNetCoreCLI@2 + displayName: Nuget Test + inputs: + command: test + projects: '**/*Tests.csproj' + arguments: '--configuration ${{ variables.buildConfiguration }} --collect "Code coverage"' + + - ${{ if eq(variables['Build.SourceBranchName'], 'main') }}: + - task: NuGetCommand@2 + displayName: NuGet Push + inputs: + command: 'push' + packagesToPush: '$(Pipeline.Workspace)/**/*.nupkg' + publishVstsFeed: 'LCU-Internal' + allowPackageConflicts: true + + # Deploy Evaluation dacpac + - ${{ if or(eq(variables['Build.SourceBranchName'], 'develop'), eq(variables['Build.SourceBranchName'], 'main')) }}: + - stage: DB_CD_Evaluation_Dacpac + displayName: Deploy Evaluation db changes + dependsOn: + - dotnet_nuget + jobs: + - deployment: db_deploy + displayName: Deploy DB dacpac + pool: $(pvar_azdo_agent_pool_db) + environment: + name: $(pvar_azdo_deployment_environment_db) + resourceType: virtualMachine + tags: sqldb + strategy: + runOnce: + deploy: + steps: + + - task: Powershell@2 + displayName: Output Run Info + inputs: + targetType: inline + script: | + Write-Host "##vso[task.logissue type=warning] Deploying to SQL Server: $(pvar_loanrate_sql_server_name)" + Write-Host "##vso[task.logissue type=warning] Altering database: $(pvar_loanrate_db_name)" + + - download: current + artifact: Evaluation_Dacpac + + - task: SqlDacpacDeploymentOnMachineGroup@0 + displayName: 'Deploy dacpac' + inputs: + DacpacFile: '$(Pipeline.Workspace)/Evaluation_Dacpac/LCU.Library.Internal.LcuEvaluationSql.dacpac' + ServerName: $(pvar_loanrate_sql_server_name) + DatabaseName: $(pvar_loanrate_db_name) + + # Build LoanRate dacpac + - stage: DB_CI_LoanRate_Dacpac + dependsOn: dotnet_nuget + jobs: + - job: db_build_push + displayName: DB dacpac - Build + pool: + name: $(pvar_azdo_agent_pool_db) + demands: + - SqlPackage + - MSBuild + steps: + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifactName: Evaluation_Dacpac + itemPattern: "*.dacpac" + targetPath: './LCU.API.Internal.LoanRate/bin/${{ variables.buildConfiguration }}/' + + - task: MSBuild@1 + displayName: Build sql project + inputs: + solution: './LCU.API.Internal.LoanRateSql/LCU.API.Internal.LoanRateSql.sqlproj' + configuration: '${{ variables.buildConfiguration }}' + + - ${{ if or(eq(variables['Build.SourceBranchName'], 'main'), eq(variables['Build.SourceBranchName'], 'develop')) }}: + - publish: './LCU.API.Internal.LoanRateSql/bin/${{ variables.buildConfiguration }}/LCU.API.Internal.LoanRateSql.dacpac' + artifact: LoanRate_Dacpac + + # Deploy LoanRate dacpac + - ${{ if or(eq(variables['Build.SourceBranchName'], 'main'), eq(variables['Build.SourceBranchName'], 'develop')) }}: + - stage: DB_CD_LoanRate_Dacpac + displayName: Deploy DB changes + variables: + - name: dacpac_file_name + value: ${{ split(format('./{0}', './LCU.API.Internal.LoanRateSql/bin/${{ variables.buildConfiguration }}/LCU.API.Internal.LoanRateSql.dacpac'),'/')[length(split('./LCU.API.Internal.LoanRateSql/bin/${{ variables.buildConfiguration }}/LCU.API.Internal.LoanRateSql.dacpac','/'))] }} + dependsOn: + - DB_CI_LoanRate_Dacpac + jobs: + - deployment: db_deploy + displayName: Deploy DB dacpac + pool: $(pvar_azdo_agent_pool_db) + environment: + name: $(pvar_azdo_deployment_environment_db) + resourceType: virtualMachine + tags: sqldb + strategy: + runOnce: + deploy: + steps: + - task: Powershell@2 + displayName: Output Run Info + inputs: + targetType: inline + script: | + Write-Host "##vso[task.logissue type=warning] Deploying to SQL Server: $(pvar_loanrate_sql_server_name)" + Write-Host "##vso[task.logissue type=warning] Altering database: $(pvar_loanrate_db_name)" + + - download: current + artifact: LoanRate_Dacpac + + - task: SqlDacpacDeploymentOnMachineGroup@0 + displayName: 'Deploy dacpac' + inputs: + DacpacFile: '$(Pipeline.Workspace)/LoanRate_Dacpac/${{ variables.dacpac_file_name }}' + ServerName: $(pvar_loanrate_sql_server_name) + DatabaseName: $(pvar_loanrate_db_name) + + # Build and deploy LoanRateLoadTest dacpac + - template: db-build-deploy.yaml@azdo_pipeline_templates + parameters: + azdo_agent_pool: $(pvar_azdo_agent_pool_db) + azdo_deployment_environment: $(pvar_azdo_deployment_environment_db) + sql_project_path: './LCU.API.Internal.LoanRateLoadTestingSql/LCU.API.Internal.LoanRateLoadTestingSql.sqlproj' + built_sql_dacpac_file_path: './LCU.API.Internal.LoanRateLoadTestingSql/bin/${{ variables.buildConfiguration }}/LCU.API.Internal.LoanRateLoadTestingSql.dacpac' + name_of_artifact: loanrateloadtest_dacpac + db_name: $(pvar_loanrate_loadtest_db_name) + sql_server_name: $(pvar_loanrate_loadtest_sql_server_name) + + # DOCKER BUILD AND PUSH + - template: docker-build-push.yaml@azdo_pipeline_templates + parameters: + azdo_agent_pool: $(pvar_azdo_agent_pool) + docker_registry_service_connection: $(pvar_docker_registry_service_connection) + docker_container_repository_name: $(docker_container_repository_name) + dockerfile_path: ./Dockerfile + docker_image_tags: | + build-$(Build.BuildId) + $(source_branch_name) + + # K8S DEPLOY + - template: k8s-deploy.yaml@azdo_pipeline_templates + parameters: + dependsOn: CI + condition: or(eq(variables['Build.SourceBranchName'], 'develop'), eq(variables['Build.SourceBranchName'], 'main')) + azdo_agent_pool: $(pvar_azdo_agent_pool) + kubectl_version: $(pvar_kubectl_version) + k8s_service_connection: $(pvar_k8s_service_connection) + k8s_namespace: ${{ parameters.k8s_namespace }} + k8s_kustomization_path: ./kustomize/overlays/${{ parameters.target_environment }} + docker_registry_service_connection: $(pvar_docker_registry_service_connection) + docker_image_to_deploy: $(docker_container_repository_name):build-$(Build.BuildId) diff --git a/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-feature-branches.yaml b/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-feature-branches.yaml new file mode 100644 index 0000000..ee44aef --- /dev/null +++ b/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-feature-branches.yaml @@ -0,0 +1,10 @@ +trigger: + branches: + include: + - '*' + exclude: + - 'main' + - 'develop' +extends: + template: azure-k8s-pipeline.yaml + diff --git a/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-prd.yaml b/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-prd.yaml new file mode 100644 index 0000000..7d13684 --- /dev/null +++ b/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-prd.yaml @@ -0,0 +1,9 @@ +trigger: + branches: + include: + - 'main' +extends: + template: azure-k8s-pipeline.yaml + parameters: + target_environment: prd + k8s_namespace: landmarkcu diff --git a/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-qa.yaml b/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-qa.yaml new file mode 100644 index 0000000..f581dd4 --- /dev/null +++ b/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-qa.yaml @@ -0,0 +1,9 @@ +trigger: + branches: + include: + - 'develop' +extends: + template: azure-k8s-pipeline.yaml + parameters: + target_environment: qa + k8s_namespace: landmarkcu diff --git a/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-uat.yaml b/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-uat.yaml new file mode 100644 index 0000000..4747234 --- /dev/null +++ b/LCU.API.Internal.LoanRate/azdo_pipelines/azure-pipelines-uat.yaml @@ -0,0 +1,9 @@ +trigger: + branches: + include: + - 'main' +extends: + template: azure-k8s-pipeline.yaml + parameters: + target_environment: uat + k8s_namespace: landmarkcu diff --git a/LCU.API.Internal.LoanRate/azdo_pipelines/config-files/prd-pvars.yaml b/LCU.API.Internal.LoanRate/azdo_pipelines/config-files/prd-pvars.yaml new file mode 100644 index 0000000..e18fe88 --- /dev/null +++ b/LCU.API.Internal.LoanRate/azdo_pipelines/config-files/prd-pvars.yaml @@ -0,0 +1,18 @@ +--- +variables: + pvar_azdo_agent_pool: vmss-sub-df-lcu-apps-prd-lz-uscz + pvar_docker_registry_service_connection: Docker Registry LMCU + pvar_k8s_service_connection: svcon-sub-df-lcu-apps-prd-aks-k8s-uscz + pvar_kubectl_version: 1.25.9 + pvar_azdo_agent_pool_nuget: LCUOnPrem + pvar_azdo_agent_pool_db: LCUOnPrem + pvar_azdo_deployment_environment_db: DB-PRD + + pvar_LoanRateEvaluation_db_name: LoanRate + pvar_LoanRateEvaluation_sql_server_name: D1CLS01P + + pvar_loanrate_db_name: LoanRate + pvar_loanrate_sql_server_name: D1CLS01P + + pvar_loanrate_loadtest_db_name: LoanRateLoadTest + pvar_loanrate_loadtest_sql_server_name: D1CLS01P \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/azdo_pipelines/config-files/qa-pvars.yaml b/LCU.API.Internal.LoanRate/azdo_pipelines/config-files/qa-pvars.yaml new file mode 100644 index 0000000..215ea43 --- /dev/null +++ b/LCU.API.Internal.LoanRate/azdo_pipelines/config-files/qa-pvars.yaml @@ -0,0 +1,19 @@ +--- +variables: + pvar_azdo_agent_pool: vmss-sub-df-lcu-apps-qa-lz-uscz + pvar_docker_registry_service_connection: Docker Registry LMCU + pvar_k8s_service_connection: svcon-sub-df-lcu-apps-qa-aks-k8s-uscz + pvar_kubectl_version: 1.25.9 + pvar_azdo_agent_pool_nuget: LCUOnPrem + pvar_azdo_agent_pool_db: LCUOnPrem + pvar_azdo_deployment_environment_db: DB-QA + + pvar_LoanRateEvaluation_db_name: LoanRate + pvar_LoanRateEvaluation_sql_server_name: D1DTB01Q + + pvar_loanrate_db_name: LoanRate + pvar_loanrate_sql_server_name: D1DTB01Q + + pvar_loanrate_loadtest_db_name: LoanRateLoadTest + pvar_loanrate_loadtest_sql_server_name: D1DTB01Q + \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/azdo_pipelines/config-files/uat-pvars.yaml b/LCU.API.Internal.LoanRate/azdo_pipelines/config-files/uat-pvars.yaml new file mode 100644 index 0000000..33c1268 --- /dev/null +++ b/LCU.API.Internal.LoanRate/azdo_pipelines/config-files/uat-pvars.yaml @@ -0,0 +1,18 @@ +--- +variables: + pvar_azdo_agent_pool: vmss-sub-df-lcu-apps-uat-lz-uscz + pvar_docker_registry_service_connection: Docker Registry LMCU + pvar_k8s_service_connection: svcon-sub-df-lcu-apps-uat-aks-k8s-uscz + pvar_kubectl_version: 1.25.9 + pvar_azdo_agent_pool_nuget: LCUOnPrem + pvar_azdo_agent_pool_db: LCUOnPrem + pvar_azdo_deployment_environment_db: DB-UAT + + pvar_LoanRateEvaluation_db_name: LoanRate + pvar_LoanRateEvaluation_sql_server_name: D1CLS01U + + pvar_loanrate_db_name: LoanRate + pvar_loanrate_sql_server_name: D1CLS01U + + pvar_loanrate_loadtest_db_name: LoanRateLoadTest + pvar_loanrate_loadtest_sql_server_name: D1CLS01U \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/cleanbuildrun.sh b/LCU.API.Internal.LoanRate/cleanbuildrun.sh new file mode 100644 index 0000000..94eaff7 --- /dev/null +++ b/LCU.API.Internal.LoanRate/cleanbuildrun.sh @@ -0,0 +1,10 @@ +#!/bin/bash +echo "Building Loan Rate API Docker Image"; +dotnet clean +rm -rf LCU.API.Internal.LoanRate/bin +rm -rf LCU.API.Internal.LoanRate/obj +docker build -t landmarkcu/loan-rate . +echo "Running Loan Rate API Docker Container"; +docker run -p 8080:80 landmarkcu/loan-rate + + diff --git a/LCU.API.Internal.LoanRate/kustomize/base/Deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/base/Deployment.yaml new file mode 100644 index 0000000..fe45253 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/base/Deployment.yaml @@ -0,0 +1,44 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: loan-rate-api +spec: + replicas: 0 + selector: + matchLabels: + app: loan-rate-api + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: loan-rate-api + spec: + volumes: [] + containers: + - name: loan-rate-api + env: [] + volumeMounts: [] + image: loan-rate-api + resources: + limits: + memory: "1536Mi" + cpu: "500m" + ports: + - containerPort: 8080 + livenessProbe: + initialDelaySeconds: 5 + periodSeconds: 5 + httpGet: + path: /api/status + port: 8080 + readinessProbe: + initialDelaySeconds: 5 + periodSeconds: 5 + httpGet: + path: /api/status + port: 8080 diff --git a/LCU.API.Internal.LoanRate/kustomize/base/Service.yaml b/LCU.API.Internal.LoanRate/kustomize/base/Service.yaml new file mode 100644 index 0000000..ff0457d --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/base/Service.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: loan-rate-api +spec: + selector: + app: loan-rate-api + ports: + - port: 8080 + targetPort: 8080 diff --git a/LCU.API.Internal.LoanRate/kustomize/base/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/base/kustomization.yaml new file mode 100644 index 0000000..fb8f9ca --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/base/kustomization.yaml @@ -0,0 +1,13 @@ +resources: +- Deployment.yaml +- Service.yaml +- virtual-service.yaml +- secrets-provider-class.yaml +patches: +- path: patches/add_lcu_internal_cert.yaml + target: + kind: Deployment +images: +- name: loan-rate-api + newName: landmarkcu/lcu.api.internal.loanrate-loanrate + diff --git a/LCU.API.Internal.LoanRate/kustomize/base/patches/add_lcu_internal_cert.yaml b/LCU.API.Internal.LoanRate/kustomize/base/patches/add_lcu_internal_cert.yaml new file mode 100644 index 0000000..207f601 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/base/patches/add_lcu_internal_cert.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-lcu-internal-cert + configMap: + name: configmap-lcu-internal-cert + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-lcu-internal-cert + mountPath: /etc/ssl/certs/lcu-internal-cert.pem diff --git a/LCU.API.Internal.LoanRate/kustomize/base/secrets-provider-class.yaml b/LCU.API.Internal.LoanRate/kustomize/base/secrets-provider-class.yaml new file mode 100644 index 0000000..eb4f806 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/base/secrets-provider-class.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: loan-rate-api-csi-key-vault-secrets-provider +spec: + provider: azure + parameters: + usePodIdentity: "false" + useVMManagedIdentity: "true" + tenantId: 63cd1554-2783-4a86-ae50-b9e6c21c9499 + \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/base/virtual-service.yaml b/LCU.API.Internal.LoanRate/kustomize/base/virtual-service.yaml new file mode 100644 index 0000000..23c2ad5 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/base/virtual-service.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: loan-rate-api +spec: + hosts: [] + gateways: + - istio-system/ingress-gateway + http: + - name: loan-rate-api + match: + - uri: + prefix: / + route: + - destination: + host: loan-rate-api + port: + number: 8080 diff --git a/LCU.API.Internal.LoanRate/kustomize/components/keyvaults/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/components/keyvaults/kustomization.yaml new file mode 100644 index 0000000..0ae1552 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/keyvaults/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +patches: + - target: + kind: Deployment + path: set_secrets_deployment.yaml + - target: + kind: SecretProviderClass + path: set_secrets_provider.yaml diff --git a/LCU.API.Internal.LoanRate/kustomize/components/keyvaults/set_secrets_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/components/keyvaults/set_secrets_deployment.yaml new file mode 100644 index 0000000..5e39beb --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/keyvaults/set_secrets_deployment.yaml @@ -0,0 +1,57 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: loan-rate-api-secrets + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: normal-loan-rate-api-csi-key-vault-secrets-provider + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: loan-rate-api-secrets + mountPath: /vault/secrets/loan-rate-queue-password.json + readOnly: true + subPath: loan-rate-queue-password + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: loan-rate-api-secrets + mountPath: /vault/secrets/loan-rate-queue-username.json + readOnly: true + subPath: loan-rate-queue-username + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: loan-rate-api-secrets + mountPath: /vault/secrets/loan-rate-seq-api-key.json + readOnly: true + subPath: loan-rate-seq-api-key + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: loan-rate-api-secrets + mountPath: /vault/secrets/loan-rate-loan-rate-connection-string.json + readOnly: true + subPath: loan-rate-loan-rate-connection-string + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: loan-rate-api-secrets + mountPath: /vault/secrets/loan-rate-load-test-connection-string.json + readOnly: true + subPath: loan-rate-load-test-connection-string + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: loan-rate-api-secrets + mountPath: /vault/secrets/loan-rate-evaluation-connection-string.json + readOnly: true + subPath: loan-rate-evaluation-connection-string diff --git a/LCU.API.Internal.LoanRate/kustomize/components/keyvaults/set_secrets_provider.yaml b/LCU.API.Internal.LoanRate/kustomize/components/keyvaults/set_secrets_provider.yaml new file mode 100644 index 0000000..8db1c81 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/keyvaults/set_secrets_provider.yaml @@ -0,0 +1,28 @@ +- op: add + path: /spec/parameters/objects + value: | + array: + - | + objectName: loan-rate-queue-password + objectType: secret + objectVersion: "" + - | + objectName: loan-rate-queue-username + objectType: secret + objectVersion: "" + - | + objectName: loan-rate-seq-api-key + objectType: secret + objectVersion: "" + - | + objectName: loan-rate-loan-rate-connection-string + objectType: secret + objectVersion: "" + - | + objectName: loan-rate-load-test-connection-string + objectType: secret + objectVersion: "" + - | + objectName: loan-rate-evaluation-connection-string + objectType: secret + objectVersion: "" \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_initiate/appsettings/appsettings.json b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_initiate/appsettings/appsettings.json new file mode 100644 index 0000000..e9b5dc7 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_initiate/appsettings/appsettings.json @@ -0,0 +1,9 @@ +{ + "Primary": { + "FeatureFlags": { + "RunLoadTestInitiateConsumer": true, + "RunLoadTestProcessRateConsumer": false, + "RunLoadTestRateCompletionConsumer": false + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_initiate/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_initiate/kustomization.yaml new file mode 100644 index 0000000..42e0553 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_initiate/kustomization.yaml @@ -0,0 +1,39 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +namePrefix: load-test-initiate- + +configMapGenerator: + - name: loanrateapi-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + +replacements: + - source: + kind: Deployment + fieldPath: metadata.name + targets: + - select: + kind: Deployment + fieldPaths: + - spec.selector.matchLabels.app + - spec.template.metadata.labels.app + - spec.template.spec.containers.*.name + + - select: + kind: Service + fieldPaths: + - spec.selector.app + + - select: + kind: VirtualService + fieldPaths: + - spec.http.*.route.*.destination.host + - spec.http.*.name diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_initiate/set_appsettings_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_initiate/set_appsettings_deployment.yaml new file mode 100644 index 0000000..2657d55 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_initiate/set_appsettings_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-appsettings + configMap: + name: loanrateapi-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-appsettings + mountPath: /app/config/mapped-appsettings diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_process_rate/appsettings/appsettings.json b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_process_rate/appsettings/appsettings.json new file mode 100644 index 0000000..5bd2db6 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_process_rate/appsettings/appsettings.json @@ -0,0 +1,10 @@ +{ + "Primary": { + "FeatureFlags": { + "RunLoadTestInitiateConsumer": false, + "RunLoadTestProcessRateConsumer": true, + "RunLoadTestRateCompletionConsumer": false + } + } + +} diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_process_rate/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_process_rate/kustomization.yaml new file mode 100644 index 0000000..ba1f08f --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_process_rate/kustomization.yaml @@ -0,0 +1,39 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +namePrefix: load-test-process-rate- + +configMapGenerator: + - name: loanrateapi-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + +replacements: + - source: + kind: Deployment + fieldPath: metadata.name + targets: + - select: + kind: Deployment + fieldPaths: + - spec.selector.matchLabels.app + - spec.template.metadata.labels.app + - spec.template.spec.containers.*.name + + - select: + kind: Service + fieldPaths: + - spec.selector.app + + - select: + kind: VirtualService + fieldPaths: + - spec.http.*.route.*.destination.host + - spec.http.*.name diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_process_rate/set_appsettings_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_process_rate/set_appsettings_deployment.yaml new file mode 100644 index 0000000..2657d55 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_process_rate/set_appsettings_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-appsettings + configMap: + name: loanrateapi-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-appsettings + mountPath: /app/config/mapped-appsettings diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_rate_completion/appsettings/appsettings.json b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_rate_completion/appsettings/appsettings.json new file mode 100644 index 0000000..a6710e8 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_rate_completion/appsettings/appsettings.json @@ -0,0 +1,9 @@ +{ + "Primary": { + "FeatureFlags": { + "RunLoadTestInitiateConsumer": false, + "RunLoadTestProcessRateConsumer": false, + "RunLoadTestRateCompletionConsumer": true + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_rate_completion/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_rate_completion/kustomization.yaml new file mode 100644 index 0000000..7f0a3ff --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_rate_completion/kustomization.yaml @@ -0,0 +1,39 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +namePrefix: load-test-rate-completion- + +configMapGenerator: + - name: loanrateapi-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + +replacements: + - source: + kind: Deployment + fieldPath: metadata.name + targets: + - select: + kind: Deployment + fieldPaths: + - spec.selector.matchLabels.app + - spec.template.metadata.labels.app + - spec.template.spec.containers.*.name + + - select: + kind: Service + fieldPaths: + - spec.selector.app + + - select: + kind: VirtualService + fieldPaths: + - spec.http.*.route.*.destination.host + - spec.http.*.name diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_rate_completion/set_appsettings_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_rate_completion/set_appsettings_deployment.yaml new file mode 100644 index 0000000..2657d55 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/load_test_rate_completion/set_appsettings_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-appsettings + configMap: + name: loanrateapi-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-appsettings + mountPath: /app/config/mapped-appsettings diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/normal/appsettings/appsettings.json b/LCU.API.Internal.LoanRate/kustomize/components/workloads/normal/appsettings/appsettings.json new file mode 100644 index 0000000..6657a52 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/normal/appsettings/appsettings.json @@ -0,0 +1,9 @@ +{ + "Primary": { + "FeatureFlags": { + "RunLoadTestInitiateConsumer": false, + "RunLoadTestProcessRateConsumer": false, + "RunLoadTestRateCompletionConsumer": false + } + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/normal/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/components/workloads/normal/kustomization.yaml new file mode 100644 index 0000000..961b506 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/normal/kustomization.yaml @@ -0,0 +1,39 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +namePrefix: normal- + +configMapGenerator: + - name: loanrateapi-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + +replacements: + - source: + kind: Deployment + fieldPath: metadata.name + targets: + - select: + kind: Deployment + fieldPaths: + - spec.selector.matchLabels.app + - spec.template.metadata.labels.app + - spec.template.spec.containers.*.name + + - select: + kind: Service + fieldPaths: + - spec.selector.app + + - select: + kind: VirtualService + fieldPaths: + - spec.http.*.route.*.destination.host + - spec.http.*.name diff --git a/LCU.API.Internal.LoanRate/kustomize/components/workloads/normal/set_appsettings_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/components/workloads/normal/set_appsettings_deployment.yaml new file mode 100644 index 0000000..2657d55 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/components/workloads/normal/set_appsettings_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-appsettings + configMap: + name: loanrateapi-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-appsettings + mountPath: /app/config/mapped-appsettings diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/local/configmap.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/local/configmap.yaml new file mode 100644 index 0000000..09c1fcf --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/local/configmap.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: loanrateapi-configmap +data: + primary.json: | + { + "Primary": { + "SeqUrl": "https://qa-seq.lcu-internal.com", + "CorsAllowedOrigins": [ + "http://localhost:3000", + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://qa-api.lcu-internal.com" + ] + }, + "FileOperationsApiConfiguration": { + "Url": "https://qa-fileoperationsapi.lcu-internal.com" + } + } diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/local/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/local/kustomization.yaml new file mode 100644 index 0000000..96cf06a --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/local/kustomization.yaml @@ -0,0 +1,8 @@ +resources: + - configmap.yaml +bases: + - ../../base +patchesStrategicMerge: + - set_appsettings_deployment.yaml + - set_deployment_image.yaml +namespace: landmarkcu diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/local/set_appsettings_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/local/set_appsettings_deployment.yaml new file mode 100644 index 0000000..d10c3ef --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/local/set_appsettings_deployment.yaml @@ -0,0 +1,12 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: loan-rate-api +spec: + template: + spec: + containers: + - name: loan-rate-api + env: + - name: ASPNETCORE_ENVIRONMENT + value: "Development" diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/local/set_deployment_image.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/local/set_deployment_image.yaml new file mode 100644 index 0000000..fd24941 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/local/set_deployment_image.yaml @@ -0,0 +1,10 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: loan-rate-api +spec: + template: + spec: + containers: + - name: loan-rate-api + image: local/loan-rate-api:1.0 diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/appsettings/appsettings.json b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/appsettings/appsettings.json new file mode 100644 index 0000000..9c72e7d --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/appsettings/appsettings.json @@ -0,0 +1,17 @@ +{ + "Primary": { + "SeqUrl": "https://seq.lcu-internal.com", + "CorsAllowedOrigins": [ + "http://localhost:3000", + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://api.lcu-internal.com" + ] + }, + "ConnectionConfiguration": { + "HostUrl": "rabbitmq.lcu-internal.com" + }, + "FileOperationsApiConfiguration": { + "Url": "https://fileoperationsapi.lcu-internal.com" + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/components/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/components/kustomization.yaml new file mode 100644 index 0000000..7f05869 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/components/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + - target: + kind: SecretProviderClass + path: set_secrets_provider_class.yaml diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/components/set_appsettings_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/components/set_appsettings_deployment.yaml new file mode 100644 index 0000000..b2d342c --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/components/set_appsettings_deployment.yaml @@ -0,0 +1,5 @@ +- op: add + path: /spec/template/spec/containers/0/env/- + value: + name: ASPNETCORE_ENVIRONMENT + value: Production \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/components/set_secrets_provider_class.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/components/set_secrets_provider_class.yaml new file mode 100644 index 0000000..816f14c --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/components/set_secrets_provider_class.yaml @@ -0,0 +1,7 @@ +- op: add + path: /spec/parameters/userAssignedIdentityID + value: 9eaefc49-e5d3-48d4-a2b9-b42cf2d8ca03 + +- op: add + path: /spec/parameters/keyvaultName + value: kv-lz-apps-prod-uscz diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/kustomization.yaml new file mode 100644 index 0000000..86b76bf --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/kustomization.yaml @@ -0,0 +1,22 @@ +resources: + - normal + - load_test_initiate + - load_test_process_rate + - load_test_rate_completion + +configMapGenerator: + - name: loan-rate-api-environment-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_configmap_deployment.yaml + +labels: +- includeSelectors: false + pairs: + app_family: loan-rate-api \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_initiate/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_initiate/kustomization.yaml new file mode 100644 index 0000000..3a47070 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_initiate/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/load_test_initiate + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_initiate/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_initiate/set_virtual_service.yaml new file mode 100644 index 0000000..6a3f0c7 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_initiate/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: prd-loan-rate-load-test-initiate-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_process_rate/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_process_rate/kustomization.yaml new file mode 100644 index 0000000..53baf83 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_process_rate/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/load_test_process_rate + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_process_rate/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_process_rate/set_virtual_service.yaml new file mode 100644 index 0000000..42504b6 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_process_rate/set_virtual_service.yaml @@ -0,0 +1,4 @@ +- op: add + path: /spec/hosts/- + value: prd-loan-rate-load-test-process-rate-api.apps.azure.lcu-internal.com + diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_rate_completion/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_rate_completion/kustomization.yaml new file mode 100644 index 0000000..1b39697 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_rate_completion/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/load_test_rate_completion + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_rate_completion/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_rate_completion/set_virtual_service.yaml new file mode 100644 index 0000000..1b5084d --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/load_test_rate_completion/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: prd-loan-rate-load-test-rate-completion-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/normal/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/normal/kustomization.yaml new file mode 100644 index 0000000..e35ebfe --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/normal/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/normal + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/normal/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/normal/set_virtual_service.yaml new file mode 100644 index 0000000..809dfc4 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/normal/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: prd-loan-rate-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/prd/set_configmap_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/set_configmap_deployment.yaml new file mode 100644 index 0000000..beebe11 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/prd/set_configmap_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-environment-appsettings + configMap: + name: loan-rate-api-environment-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-environment-appsettings + mountPath: /app/config/environment-appsettings diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/appsettings/appsettings.json b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/appsettings/appsettings.json new file mode 100644 index 0000000..cb3aef7 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/appsettings/appsettings.json @@ -0,0 +1,17 @@ +{ + "Primary": { + "SeqUrl": "https://qa-seq.lcu-internal.com", + "CorsAllowedOrigins": [ + "http://localhost:3000", + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://qa-api.lcu-internal.com" + ] + }, + "ConnectionConfiguration": { + "HostUrl": "qa-rabbitmq.lcu-internal.com" + }, + "FileOperationsApiConfiguration": { + "Url": "https://qa-fileoperationsapi.lcu-internal.com" + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/components/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/components/kustomization.yaml new file mode 100644 index 0000000..7f05869 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/components/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + - target: + kind: SecretProviderClass + path: set_secrets_provider_class.yaml diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/components/set_appsettings_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/components/set_appsettings_deployment.yaml new file mode 100644 index 0000000..818dec4 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/components/set_appsettings_deployment.yaml @@ -0,0 +1,5 @@ +- op: add + path: /spec/template/spec/containers/0/env/- + value: + name: ASPNETCORE_ENVIRONMENT + value: Development \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/components/set_secrets_provider_class.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/components/set_secrets_provider_class.yaml new file mode 100644 index 0000000..8719a77 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/components/set_secrets_provider_class.yaml @@ -0,0 +1,7 @@ +- op: add + path: /spec/parameters/userAssignedIdentityID + value: 3286ed9a-f85c-4c50-b742-dbd3c1c3fe1f + +- op: add + path: /spec/parameters/keyvaultName + value: kv-lz-apps-qa-uscz diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/kustomization.yaml new file mode 100644 index 0000000..86b76bf --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/kustomization.yaml @@ -0,0 +1,22 @@ +resources: + - normal + - load_test_initiate + - load_test_process_rate + - load_test_rate_completion + +configMapGenerator: + - name: loan-rate-api-environment-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_configmap_deployment.yaml + +labels: +- includeSelectors: false + pairs: + app_family: loan-rate-api \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_initiate/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_initiate/kustomization.yaml new file mode 100644 index 0000000..3a47070 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_initiate/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/load_test_initiate + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_initiate/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_initiate/set_virtual_service.yaml new file mode 100644 index 0000000..61e08d7 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_initiate/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: qa-loan-rate-load-test-initiate-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_process_rate/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_process_rate/kustomization.yaml new file mode 100644 index 0000000..53baf83 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_process_rate/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/load_test_process_rate + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_process_rate/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_process_rate/set_virtual_service.yaml new file mode 100644 index 0000000..cf3811d --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_process_rate/set_virtual_service.yaml @@ -0,0 +1,4 @@ +- op: add + path: /spec/hosts/- + value: qa-loan-rate-load-test-process-rate-api.apps.azure.lcu-internal.com + diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_rate_completion/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_rate_completion/kustomization.yaml new file mode 100644 index 0000000..1b39697 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_rate_completion/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/load_test_rate_completion + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_rate_completion/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_rate_completion/set_virtual_service.yaml new file mode 100644 index 0000000..c601bfd --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/load_test_rate_completion/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: qa-loan-rate-load-test-rate-completion-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/normal/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/normal/kustomization.yaml new file mode 100644 index 0000000..e35ebfe --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/normal/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/normal + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/normal/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/normal/set_virtual_service.yaml new file mode 100644 index 0000000..7bdc2df --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/normal/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: qa-loan-rate-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/qa/set_configmap_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/set_configmap_deployment.yaml new file mode 100644 index 0000000..beebe11 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/qa/set_configmap_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-environment-appsettings + configMap: + name: loan-rate-api-environment-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-environment-appsettings + mountPath: /app/config/environment-appsettings diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/appsettings/appsettings.json b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/appsettings/appsettings.json new file mode 100644 index 0000000..693b1cf --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/appsettings/appsettings.json @@ -0,0 +1,17 @@ +{ + "Primary": { + "SeqUrl": "https://uat-seq.lcu-internal.com", + "CorsAllowedOrigins": [ + "http://localhost:3000", + "http://lcu-internal.com", + "https://lcu-internal.com", + "https://uat-api.lcu-internal.com" + ] + }, + "ConnectionConfiguration": { + "HostUrl": "uat-rabbitmq.lcu-internal.com" + }, + "FileOperationsApiConfiguration": { + "Url": "https://uat-fileoperationsapi.lcu-internal.com" + } +} \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/components/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/components/kustomization.yaml new file mode 100644 index 0000000..7f05869 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/components/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component + +patches: + - target: + kind: Deployment + path: set_appsettings_deployment.yaml + - target: + kind: SecretProviderClass + path: set_secrets_provider_class.yaml diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/components/set_appsettings_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/components/set_appsettings_deployment.yaml new file mode 100644 index 0000000..84d255b --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/components/set_appsettings_deployment.yaml @@ -0,0 +1,5 @@ +- op: add + path: /spec/template/spec/containers/0/env/- + value: + name: ASPNETCORE_ENVIRONMENT + value: Staging \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/components/set_secrets_provider_class.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/components/set_secrets_provider_class.yaml new file mode 100644 index 0000000..efe4103 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/components/set_secrets_provider_class.yaml @@ -0,0 +1,7 @@ +- op: add + path: /spec/parameters/userAssignedIdentityID + value: 64ba01ed-1ecc-4c9b-b56e-8d618f2b81ce + +- op: add + path: /spec/parameters/keyvaultName + value: kv-lz-apps-uat-uscz diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/kustomization.yaml new file mode 100644 index 0000000..86b76bf --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/kustomization.yaml @@ -0,0 +1,22 @@ +resources: + - normal + - load_test_initiate + - load_test_process_rate + - load_test_rate_completion + +configMapGenerator: + - name: loan-rate-api-environment-appsettings + files: + - appsettings/appsettings.json +generatorOptions: + disableNameSuffixHash: true + +patches: + - target: + kind: Deployment + path: set_configmap_deployment.yaml + +labels: +- includeSelectors: false + pairs: + app_family: loan-rate-api \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_initiate/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_initiate/kustomization.yaml new file mode 100644 index 0000000..3a47070 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_initiate/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/load_test_initiate + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_initiate/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_initiate/set_virtual_service.yaml new file mode 100644 index 0000000..14347b1 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_initiate/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: uat-loan-rate-load-test-initiate-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_process_rate/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_process_rate/kustomization.yaml new file mode 100644 index 0000000..53baf83 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_process_rate/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/load_test_process_rate + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_process_rate/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_process_rate/set_virtual_service.yaml new file mode 100644 index 0000000..09c6346 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_process_rate/set_virtual_service.yaml @@ -0,0 +1,4 @@ +- op: add + path: /spec/hosts/- + value: uat-loan-rate-load-test-process-rate-api.apps.azure.lcu-internal.com + diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_rate_completion/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_rate_completion/kustomization.yaml new file mode 100644 index 0000000..1b39697 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_rate_completion/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/load_test_rate_completion + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_rate_completion/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_rate_completion/set_virtual_service.yaml new file mode 100644 index 0000000..cbb93d7 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/load_test_rate_completion/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: uat-loan-rate-load-test-rate-completion-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/normal/kustomization.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/normal/kustomization.yaml new file mode 100644 index 0000000..e35ebfe --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/normal/kustomization.yaml @@ -0,0 +1,12 @@ +resources: + - ../../../base + +components: + - ../components + - ../../../components/keyvaults + - ../../../components/workloads/normal + +patches: + - target: + kind: VirtualService + path: set_virtual_service.yaml \ No newline at end of file diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/normal/set_virtual_service.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/normal/set_virtual_service.yaml new file mode 100644 index 0000000..a8728cb --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/normal/set_virtual_service.yaml @@ -0,0 +1,3 @@ +- op: add + path: /spec/hosts/- + value: uat-loan-rate-api.apps.azure.lcu-internal.com diff --git a/LCU.API.Internal.LoanRate/kustomize/overlays/uat/set_configmap_deployment.yaml b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/set_configmap_deployment.yaml new file mode 100644 index 0000000..beebe11 --- /dev/null +++ b/LCU.API.Internal.LoanRate/kustomize/overlays/uat/set_configmap_deployment.yaml @@ -0,0 +1,12 @@ +- op: add + path: /spec/template/spec/volumes/- + value: + name: volume-environment-appsettings + configMap: + name: loan-rate-api-environment-appsettings + +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + name: volume-environment-appsettings + mountPath: /app/config/environment-appsettings