Adding
This commit is contained in:
@@ -0,0 +1,330 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
**/Properties/launchSettings.json
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush
|
||||||
|
.cr/
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# .NET Core SDK
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS aspnetcore-build
|
||||||
|
|
||||||
|
# Copy Projects
|
||||||
|
COPY LCU.Api.Internal.Dispatcher/LCU.Api.Internal.Dispatcher.csproj ./source/LCU.Api.Internal.Dispatcher/
|
||||||
|
COPY LCU.Api.Internal.DispatcherLib/LCU.Api.Internal.DispatcherLib.csproj ./source/LCU.Api.Internal.DispatcherLib/
|
||||||
|
COPY LCU.Api.Internal.DispatcherSharedLib/LCU.Api.Internal.DispatcherSharedLib.csproj ./source/LCU.Api.Internal.DispatcherSharedLib/
|
||||||
|
COPY LCU.Library.Internal.DispatcherModels/LCU.Library.Internal.DispatcherModels.csproj ./source/LCU.Library.Internal.DispatcherModels/
|
||||||
|
|
||||||
|
# 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.Dispatcher/LCU.Api.Internal.Dispatcher.csproj
|
||||||
|
|
||||||
|
# Copy All Files
|
||||||
|
COPY . ./source/
|
||||||
|
|
||||||
|
FROM aspnetcore-build AS test
|
||||||
|
LABEL test=true
|
||||||
|
COPY LCU.Api.Internal.DispatcherTests/LCU.Api.Internal.DispatcherTests.csproj ./source/LCU.Api.Internal.DispatcherTests/
|
||||||
|
RUN dotnet restore ./source/LCU.Api.Internal.DispatcherTests/LCU.Api.Internal.DispatcherTests.csproj
|
||||||
|
RUN dotnet test -c Release --results-directory /testresults --logger "trx;LogFileName=test_results.trx" ./source/LCU.Api.Internal.DispatcherTests/LCU.Api.Internal.DispatcherTests.csproj
|
||||||
|
|
||||||
|
# .NET Core Publish
|
||||||
|
FROM aspnetcore-build as publish
|
||||||
|
RUN dotnet publish ./source/LCU.Api.Internal.Dispatcher/LCU.Api.Internal.Dispatcher.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.Dispatcher.dll"]
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Api.Internal.Dispatcher", "LCU.Api.Internal.Dispatcher\LCU.Api.Internal.Dispatcher.csproj", "{1C92A326-91A3-4904-B3EF-B766E10E7191}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Api.Internal.DispatcherTests", "LCU.Api.Internal.DispatcherTests\LCU.Api.Internal.DispatcherTests.csproj", "{98503299-44F3-410A-B61F-36BC1706F132}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{1C92A326-91A3-4904-B3EF-B766E10E7191} = {1C92A326-91A3-4904-B3EF-B766E10E7191}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Api.Internal.DispatcherLib", "LCU.Api.Internal.DispatcherLib\LCU.Api.Internal.DispatcherLib.csproj", "{0D3255ED-449B-4F11-861B-5C9479520ED5}"
|
||||||
|
EndProject
|
||||||
|
Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "LCU.Api.Internal.DispatcherSql", "LCU.Api.Internal.DispatcherSql\LCU.Api.Internal.DispatcherSql.sqlproj", "{B82ADFBD-F6BD-431A-88CE-5540190D225E}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Api.Internal.DispatcherSharedLib", "LCU.Api.Internal.DispatcherSharedLib\LCU.Api.Internal.DispatcherSharedLib.csproj", "{1A38E17A-65AD-4CB9-84AB-A733C80DCA11}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Api.Internal.DispatcherIntegrationTests", "LCU.Api.Internal.DispatcherIntegrationTests\LCU.Api.Internal.DispatcherIntegrationTests.csproj", "{02AC7B60-3118-4D90-BDC6-654736BC82E8}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LCU.Library.Internal.DispatcherModels", "LCU.Library.Internal.DispatcherModels\LCU.Library.Internal.DispatcherModels.csproj", "{1B92770F-4729-4634-AD40-0E66B7F65432}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{1C92A326-91A3-4904-B3EF-B766E10E7191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1C92A326-91A3-4904-B3EF-B766E10E7191}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1C92A326-91A3-4904-B3EF-B766E10E7191}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1C92A326-91A3-4904-B3EF-B766E10E7191}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{98503299-44F3-410A-B61F-36BC1706F132}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{98503299-44F3-410A-B61F-36BC1706F132}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{98503299-44F3-410A-B61F-36BC1706F132}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{98503299-44F3-410A-B61F-36BC1706F132}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0D3255ED-449B-4F11-861B-5C9479520ED5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0D3255ED-449B-4F11-861B-5C9479520ED5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0D3255ED-449B-4F11-861B-5C9479520ED5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0D3255ED-449B-4F11-861B-5C9479520ED5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B82ADFBD-F6BD-431A-88CE-5540190D225E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B82ADFBD-F6BD-431A-88CE-5540190D225E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B82ADFBD-F6BD-431A-88CE-5540190D225E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||||
|
{B82ADFBD-F6BD-431A-88CE-5540190D225E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B82ADFBD-F6BD-431A-88CE-5540190D225E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B82ADFBD-F6BD-431A-88CE-5540190D225E}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||||
|
{1A38E17A-65AD-4CB9-84AB-A733C80DCA11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1A38E17A-65AD-4CB9-84AB-A733C80DCA11}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1A38E17A-65AD-4CB9-84AB-A733C80DCA11}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1A38E17A-65AD-4CB9-84AB-A733C80DCA11}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{02AC7B60-3118-4D90-BDC6-654736BC82E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{02AC7B60-3118-4D90-BDC6-654736BC82E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{02AC7B60-3118-4D90-BDC6-654736BC82E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{02AC7B60-3118-4D90-BDC6-654736BC82E8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1B92770F-4729-4634-AD40-0E66B7F65432}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1B92770F-4729-4634-AD40-0E66B7F65432}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1B92770F-4729-4634-AD40-0E66B7F65432}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1B92770F-4729-4634-AD40-0E66B7F65432}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {56A51B7E-A90A-4BFF-85D1-19ECB769AF6A}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.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 string SchedulersCronSchedule { get; set; }
|
||||||
|
public string BatchErroredJobsCronSchedule { get; set; }
|
||||||
|
public int ProcessingLimitInMinutes { get; set; }
|
||||||
|
public int NumberOfWorkItemExecutions { get; set; }
|
||||||
|
public int NumberOfExecutionMessages { get; set; }
|
||||||
|
public string BatchErroredJobsTemplate { get; set; }
|
||||||
|
public string ErrorRowTemplate { get; set; }
|
||||||
|
public string BatchErroredJobsSubject { get; set; }
|
||||||
|
public string DefaultErrorRecipient { get; set; }
|
||||||
|
public string CleanUpJobsCronSchedule { get; set; }
|
||||||
|
public int CleanUpJobsDaysToDelete { get; set; }
|
||||||
|
public int DefaultDelayMilliseconds { get; set; }
|
||||||
|
public FeatureFlags FeatureFlags { get; set; }
|
||||||
|
public List<JobStatus> JobStatusesToMonitorByTimeLimit { get; set; }
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
|
||||||
|
public class ConnectionsConfiguration
|
||||||
|
{
|
||||||
|
public Sql Sql { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Sql
|
||||||
|
{
|
||||||
|
public string DispatcherDb { get; set; }
|
||||||
|
public SQLOptions SQLOptions { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SQLOptions
|
||||||
|
{
|
||||||
|
public int MaxRetryCount { get; set; }
|
||||||
|
public TimeSpan MaxRetryDelay { get; set; }
|
||||||
|
public ICollection<int> ErrorNumbersToAdd { get; set; }
|
||||||
|
public bool EnableDetailedErrors { get; set; }
|
||||||
|
public int CommandTimeout { get; set; }
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
namespace LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
|
||||||
|
public class FeatureFlags
|
||||||
|
{
|
||||||
|
public bool RunCompletionConsumer { get; set; }
|
||||||
|
public bool RunExecutionConsumer { get; set; }
|
||||||
|
public bool RunJobSchedulerHostedService { get; set; }
|
||||||
|
public bool RunBatchErroredJobsHostedService { get; set; }
|
||||||
|
public bool RunCleanUpJobsHostedService { get; set; }
|
||||||
|
public bool RunDetermineOperationConsumer { get; set; }
|
||||||
|
public bool RunJobFailureConsumer { get; set; }
|
||||||
|
public bool RunSendErroredJobAlertConsumer { get; set; }
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
using LCU.Library.Internal.LcuMassTransitModels;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
|
||||||
|
public class ReceiveEndpointConfigurations
|
||||||
|
{
|
||||||
|
public ReceiveEndpointConfiguration ExecutionReceiveEndpoint { get; set; }
|
||||||
|
public ReceiveEndpointConfiguration CompletionReceiveEndpoint { get; set; }
|
||||||
|
public ReceiveEndpointConfiguration DetermineOperationReceiveEndpoint { get; set; }
|
||||||
|
public ReceiveEndpointConfiguration JobFailureReceiveEndpoint { get; set; }
|
||||||
|
public ReceiveEndpointConfiguration SendErroredJobAlertReceiveEndpoint { get; set; }
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Library.Internal.Logging.Performance;
|
||||||
|
using MassTransit;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using ApiResponse = LCU.Library.Internal.ApiResponse.Models.Response;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Consumers;
|
||||||
|
|
||||||
|
public class CompletionConsumer : IConsumer<ApiResponse>
|
||||||
|
{
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
|
||||||
|
public CompletionConsumer(IServiceScopeFactory scopeFactory)
|
||||||
|
{
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Consume(ConsumeContext<ApiResponse> context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = _scopeFactory.CreateAsyncScope();
|
||||||
|
var dispatcherReturnResponse = JsonConvert.DeserializeObject<DispatcherReturnResponse>(context.Message.ResponsePayload);
|
||||||
|
var performanceMonitor = new PerformanceMonitor("DispatcherCompletionConsumerMonitor",
|
||||||
|
Guid.NewGuid());
|
||||||
|
|
||||||
|
await scope.ServiceProvider.GetRequiredService<ICompletionService>().HandleCompletionRequest(context.Message);
|
||||||
|
performanceMonitor.LogPerformance((new Dictionary<string, string>(){
|
||||||
|
{"WorkItemId", dispatcherReturnResponse.WorkItemId.ToString()}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch (ServiceException serviceException)
|
||||||
|
{
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Error(serviceException, $"Dispatcher Completion Failure. " +
|
||||||
|
$"The Dispatcher Completion Request is placed on LCU.Dispatcher.Completion_error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Fatal(exception, $"Unexpected Failure");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using LCU.Library.Internal.Logging.Performance;
|
||||||
|
using MassTransit;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Consumers
|
||||||
|
{
|
||||||
|
public class DetermineOperationConsumer : IConsumer<DetermineOperationRequest>
|
||||||
|
{
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
|
||||||
|
public DetermineOperationConsumer(IServiceScopeFactory scopeFactory)
|
||||||
|
{
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Consume(ConsumeContext<DetermineOperationRequest> context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = _scopeFactory.CreateAsyncScope();
|
||||||
|
var performanceMonitor = new PerformanceMonitor("DispatcherDetermineOperationConsumerMonitor", Guid.NewGuid());
|
||||||
|
await scope.ServiceProvider.GetRequiredService<IDetermineOperationService>().HandleDetermineOperationRequest(context.Message);
|
||||||
|
performanceMonitor.LogPerformance((new Dictionary<string, string>(){
|
||||||
|
{"JobId", context.Message.CurrentJob.JobId.ToString()},
|
||||||
|
{"WorkItemId", context.Message.CurrentWorkItem.WorkItemId.ToString()},
|
||||||
|
{"WorkItemExecutionId", context.Message.CurrentWorkItem?.CurrentWorkItemExecution?.WorkItemExecutionId.ToString()}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch (ServiceException serviceException)
|
||||||
|
{
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Error(serviceException, $"Dispatcher Determine Operation Failure. " +
|
||||||
|
$"The Dispatcher Determine Operation Request is placed on LCU.Dispatcher.DetermineOperation_error.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Fatal(exception, $"Unexpected Dispatcher Determine Operation Failure. " +
|
||||||
|
$"The Dispatcher Determine Operation Request is placed on LCU.Dispatcher.DetermineOperation_error.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
using MassTransit;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using LCU.Library.Internal.Logging.Performance;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Consumers;
|
||||||
|
|
||||||
|
public class ExecutionConsumer : IConsumer<WorkItemExecution>
|
||||||
|
{
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
|
||||||
|
public ExecutionConsumer(IServiceScopeFactory scopeFactory)
|
||||||
|
{
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Consume(ConsumeContext<WorkItemExecution> context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = _scopeFactory.CreateAsyncScope();
|
||||||
|
var performanceMonitor = new PerformanceMonitor("DispatcherExecutionConsumerMonitor", Guid.NewGuid());
|
||||||
|
await scope.ServiceProvider.GetRequiredService<IExecutionService>().HandleExecutionRequest(context.Message);
|
||||||
|
performanceMonitor.LogPerformance((new Dictionary<string, string>(){
|
||||||
|
{"WorkItemId", context.Message.WorkItemId.ToString()},
|
||||||
|
{"WorkItemExecutionId", context.Message.WorkItemExecutionId.ToString()}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch (ServiceException serviceException)
|
||||||
|
{
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Error(serviceException, $"Dispatcher Execution Failure. " +
|
||||||
|
$"The Dispatcher Execution Request is placed on LCU.Dispatcher.Execution_error.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Fatal(exception, $"Unexpected Dispatcher Execution Failure. " +
|
||||||
|
$"The Dispatcher Execution Request is placed on LCU.Dispatcher.Execution_error.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using LCU.Library.Internal.Logging.Performance;
|
||||||
|
using MassTransit;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Consumers
|
||||||
|
{
|
||||||
|
public class JobFailureConsumer : IConsumer<JobFailureRequest>
|
||||||
|
{
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
|
||||||
|
public JobFailureConsumer(IServiceScopeFactory scopeFactory)
|
||||||
|
{
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Consume(ConsumeContext<JobFailureRequest> context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = _scopeFactory.CreateAsyncScope();
|
||||||
|
var performanceMonitor = new PerformanceMonitor("DispatcherJobFailureConsumerMonitor", Guid.NewGuid());
|
||||||
|
await scope.ServiceProvider.GetRequiredService<IJobFailureService>().HandleJobFailureRequest(context.Message);
|
||||||
|
performanceMonitor.LogPerformance((new Dictionary<string, string>(){
|
||||||
|
{"JobId", context.Message.JobId.ToString()},
|
||||||
|
{"WorkItemId", context.Message.WorkItemId.ToString()}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch (ServiceException serviceException)
|
||||||
|
{
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Error(serviceException, $"Dispatcher Job Failure. " +
|
||||||
|
$"The Dispatcher Job Failure Request is placed on LCU.Dispatcher.JobFailure_error.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Fatal(exception, $"Unexpected Dispatcher Job Failure. " +
|
||||||
|
$"The Dispatcher Job Failure Request is placed on LCU.Dispatcher.JobFailure_error.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using LCU.Library.Internal.Logging.Performance;
|
||||||
|
using MassTransit;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Consumers
|
||||||
|
{
|
||||||
|
public class SendErroredJobAlertConsumer : IConsumer<Batch<BatchedErroredJobAlertRequest>>
|
||||||
|
{
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
|
||||||
|
public SendErroredJobAlertConsumer(IServiceScopeFactory scopeFactory)
|
||||||
|
{
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Consume(ConsumeContext<Batch<BatchedErroredJobAlertRequest>> context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = _scopeFactory.CreateAsyncScope();
|
||||||
|
var performanceMonitor = new PerformanceMonitor("DispatcherBatchErroredJobsConsumerMonitor", Guid.NewGuid());
|
||||||
|
|
||||||
|
var batchedErroredJobAlertRequestlist = new List<BatchedErroredJobAlertRequest>();
|
||||||
|
for (int index = 0; index < context.Message.Length; index++)
|
||||||
|
{
|
||||||
|
ConsumeContext<BatchedErroredJobAlertRequest> messageContext = context.Message[index];
|
||||||
|
batchedErroredJobAlertRequestlist.Add(messageContext.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
await scope.ServiceProvider.GetRequiredService<ISendErroredJobAlertService>().HandleSendingErroredJobAlerts(batchedErroredJobAlertRequestlist);
|
||||||
|
performanceMonitor.LogPerformance((new Dictionary<string, string>(){
|
||||||
|
{"NumberOfBatchedErrors", batchedErroredJobAlertRequestlist.Count().ToString()}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch (ServiceException serviceException)
|
||||||
|
{
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Error(serviceException, $"Dispatcher Send Errored Job Alert. " +
|
||||||
|
$"The Dispatcher Send Errored Job Alert Request is placed on LCU.Dispatcher.SendErroredJobAlert_error.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Fatal(exception, $"Unexpected Dispatcher Send Errored Job Alert. " +
|
||||||
|
$"The Dispatcher Send Errored Job Alert Request is placed on LCU.Dispatcher.SendErroredJobAlert_error.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class CleanUpJobProcessController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ICleanUpJobsService _cleanUpJobsService;
|
||||||
|
|
||||||
|
public CleanUpJobProcessController(ICleanUpJobsService cleanUpJobsService)
|
||||||
|
{
|
||||||
|
_cleanUpJobsService = cleanUpJobsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("RunCleanUpJobProcessNow", Name = "RunCleanUpJobProcessNow")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> RunCleanUpJobNow()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.Information("Manually Kicked Off Dispatcher's Clean Up Job Hosted Service.");
|
||||||
|
var token = new CancellationToken();
|
||||||
|
await _cleanUpJobsService.PerformCleanUpJobs(token);
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+161
@@ -0,0 +1,161 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class CleanUpJobSettingController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ICleanUpJobSettingService _cleanUpJobSettingService;
|
||||||
|
|
||||||
|
public CleanUpJobSettingController(ICleanUpJobSettingService cleanUpJobSettingService)
|
||||||
|
{
|
||||||
|
_cleanUpJobSettingService = cleanUpJobSettingService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetAllCleanUpJobSetting", Name = "GetAllCleanUpJobSetting")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<CleanUpJobSetting>), 200)]
|
||||||
|
[ProducesResponseType(204)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Get()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _cleanUpJobSettingService.GetAsync();
|
||||||
|
if (result == null || !result.Any())
|
||||||
|
{
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetByCleanUpJobSettingIdAsync", Name = "GetByCleanUpJobSettingIdAsync")]
|
||||||
|
[ProducesResponseType(typeof(CleanUpJobSetting), 200)]
|
||||||
|
[ProducesResponseType(204)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> GetByCleanUpJobSettingIdAsync(int cleanUpJobSettingId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (cleanUpJobSettingId <= 0)
|
||||||
|
{
|
||||||
|
return BadRequest("Invalid CleanUpJobSettingId.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _cleanUpJobSettingService.GetByCleanUpJobSettingIdAsync(cleanUpJobSettingId);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[ProducesResponseType(typeof(CleanUpJobSetting), 200)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(406)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] CleanUpJobSetting request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _cleanUpJobSettingService.SaveAsync(request);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status406NotAcceptable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut()]
|
||||||
|
[ProducesResponseType(typeof(CleanUpJobSetting), 200)]
|
||||||
|
[ProducesResponseType(406)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Put([FromBody] CleanUpJobSetting request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _cleanUpJobSettingService.UpdateAsync(request);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status406NotAcceptable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete()]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Delete(int cleanUpJobSettingId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (cleanUpJobSettingId <= 0)
|
||||||
|
{
|
||||||
|
return BadRequest("Invalid CleanUpJobSettingId");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _cleanUpJobSettingService.DeleteAsync(cleanUpJobSettingId);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+192
@@ -0,0 +1,192 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class CleanUpJobTagController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ICleanUpJobTagService _cleanUpJobTagService;
|
||||||
|
public CleanUpJobTagController(ICleanUpJobTagService cleanUpJobTagService)
|
||||||
|
{
|
||||||
|
_cleanUpJobTagService = cleanUpJobTagService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet(Name = "GetAllCleanUpJobTag")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<CleanUpJobTag>), 200)]
|
||||||
|
[ProducesResponseType(204)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Get()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _cleanUpJobTagService.GetAsync();
|
||||||
|
if (result == null || !result.Any())
|
||||||
|
{
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetByCleanUpJobTagIdAsync", Name = "GetByCleanUpJobTagIdAsync")]
|
||||||
|
[ProducesResponseType(typeof(CleanUpJobTag), 200)]
|
||||||
|
[ProducesResponseType(204)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> GetByCleanUpJobTagIdAsync(int cleanUpJobTagId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (cleanUpJobTagId <= 0)
|
||||||
|
{
|
||||||
|
return BadRequest("Invalid CleanUpJobTagIdId.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _cleanUpJobTagService.GetByCleanUpJobTagIdAsync(cleanUpJobTagId);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetTagsByCleanUpJobSettingIdAsync", Name = "GetTagsByCleanUpJobSettingIdAsync")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<CleanUpJobTag>), 200)]
|
||||||
|
[ProducesResponseType(204)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> GetByCleanUpJobSettingIdAsync(int cleanUpJobSettingId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (cleanUpJobSettingId <= 0)
|
||||||
|
{
|
||||||
|
return BadRequest("Invalid CleanUpJobSettingId.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _cleanUpJobTagService.GetByCleanUpJobSettingIdAsync(cleanUpJobSettingId);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ArgumentNullException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status404NotFound);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost()]
|
||||||
|
[ProducesResponseType(typeof(CleanUpJobTag), 200)]
|
||||||
|
[ProducesResponseType(406)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] CleanUpJobTag request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _cleanUpJobTagService.SaveAsync(request);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status406NotAcceptable);
|
||||||
|
}
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("DeleteByCleanUpJobTagId", Name = "DeleteByCleanUpJobTagId")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Delete(int cleanUpJobTagId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (cleanUpJobTagId <= 0)
|
||||||
|
{
|
||||||
|
return BadRequest("Invalid CleanUpJobTagId");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _cleanUpJobTagService.DeleteAsync(cleanUpJobTagId);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("DeleteByCleanUpJobSettingId", Name = "DeleteByCleanUpJobSettingId")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> DeleteByCleanUpJobSettingId(int cleanUpJobSettingId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (cleanUpJobSettingId <= 0)
|
||||||
|
{
|
||||||
|
return BadRequest("Invalid CleanUpJobSettingId");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _cleanUpJobTagService.DeleteByCleanUpJobSettingIdAsync(cleanUpJobSettingId);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[Route("[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class ExecutionMessageController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IExecutionMessageService _executionMessageService;
|
||||||
|
public ExecutionMessageController(IExecutionMessageService executionMessageService)
|
||||||
|
{
|
||||||
|
_executionMessageService = executionMessageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost()]
|
||||||
|
[ProducesResponseType(typeof(List<ExecutionMessage>), 200)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] List<ExecutionMessage> request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (request == null || !request.Any())
|
||||||
|
{
|
||||||
|
return BadRequest("Execution messages are required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _executionMessageService.SaveExecutionMessages(request);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+163
@@ -0,0 +1,163 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.HostedServices;
|
||||||
|
using LCU.Library.Internal.ApiSecurity.Attributes;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class HostedServicesController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ICleanUpJobsHostedService _cleanUpJobsHostedServiceStarter;
|
||||||
|
private readonly IBatchErroredJobsHostedService _batchErroredJobsServiceStarter;
|
||||||
|
private readonly IJobSchedulerHostedService _jobSchedulerHostedServiceStarter;
|
||||||
|
|
||||||
|
public HostedServicesController(ICleanUpJobsHostedService cleanUpJobsHostedService,
|
||||||
|
IBatchErroredJobsHostedService batchErroredJobsServiceStarter,
|
||||||
|
IJobSchedulerHostedService jobSchedulerHostedServiceStarter)
|
||||||
|
{
|
||||||
|
_cleanUpJobsHostedServiceStarter = cleanUpJobsHostedService;
|
||||||
|
_batchErroredJobsServiceStarter = batchErroredJobsServiceStarter;
|
||||||
|
_jobSchedulerHostedServiceStarter = jobSchedulerHostedServiceStarter;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiKey]
|
||||||
|
[HttpPost("StartCleanUpJobs", Name = "StartCleanUpJobs")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> StartCleanUpJobs()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _cleanUpJobsHostedServiceStarter.StartAsync();
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiKey]
|
||||||
|
[HttpPost("StopCleanUpJobs", Name = "StopCleanUpJobs")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> StopCleanUpJobs()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _cleanUpJobsHostedServiceStarter.StopAsync();
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiKey]
|
||||||
|
[HttpPost("StartBatchErroredJobs", Name = "StartBatchErroredJobs")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> StartBatchErroredJobs()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _batchErroredJobsServiceStarter.StartAsync();
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiKey]
|
||||||
|
[HttpPost("StopBatchErroredJobs", Name = "StopBatchErroredJobs")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> StopBatchErroredJobs()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _batchErroredJobsServiceStarter.StopAsync();
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiKey]
|
||||||
|
[HttpPost("StartJobScheduler", Name = "StartJobScheduler")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> StartJobScheduler()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _jobSchedulerHostedServiceStarter.StartAsync();
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiKey]
|
||||||
|
[HttpPost("StopJobScheduler", Name = "StopJobScheduler")]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> StopJobScheduler()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _jobSchedulerHostedServiceStarter.StopAsync();
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Extensions;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class IngestionController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IIngestionService _ingestionService;
|
||||||
|
|
||||||
|
public IngestionController(IIngestionService ingestionService)
|
||||||
|
{
|
||||||
|
_ingestionService = ingestionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/Post
|
||||||
|
[HttpPost]
|
||||||
|
[ProducesResponseType(201)]
|
||||||
|
[ProducesResponseType(typeof(List<string>), 400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] Job request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState.GetErrorMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!request.WorkItems.Any())
|
||||||
|
{
|
||||||
|
return BadRequest("Workitems are required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _ingestionService.HandleIngestionRequest(request);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class JobController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IJobService _jobService;
|
||||||
|
public JobController(IJobService jobService)
|
||||||
|
{
|
||||||
|
_jobService = jobService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("GetFilteredJobs")]
|
||||||
|
[ProducesResponseType(typeof(PagedResponse<Job>), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> GetFilteredJobs([FromBody] Filter filter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var filterResult = await _jobService.GetFilteredJobs(filter);
|
||||||
|
var result = new PagedResponse<Job>(filterResult, filter);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet(Name = "GetAllJobs")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<Job>), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Get()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobService.GetAsync();
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetInactiveJobs", Name = "GetInactiveJobs")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<Job>), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> GetInactive()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobService.GetInactiveAsync();
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetInactiveJobsExcludeSoftDeleted", Name = "GetInactiveJobsExcludeSoftDeleted")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<Job>), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> GetInactiveExcludeSoftDeleted()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobService.GetInactiveExcludeSoftDeletedTrackedAsync();
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetSoftDeletedJobs", Name = "GetSoftDeletedJobs")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<Job>), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> GetSoftDeleted(DateTime cutOffDate)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobService.GetSoftDeletedTrackedAsync(cutOffDate);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetJobByIdExcludingWorkItems", Name = "GetJobByIdExcludingWorkItems")]
|
||||||
|
[ProducesResponseType(typeof(Job), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> GetJobByIdExcludingWorkItems(int jobId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobService.GetByIdExcludingWorkItemsAsync(jobId);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("RunJobNow", Name = "RunJobNow")]
|
||||||
|
[ProducesResponseType(typeof(Job), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> RunJobNow([FromBody] Job job)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobService.RunJobNowAsync(job);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("RunJobNowById", Name = "RunJobNowById")]
|
||||||
|
[ProducesResponseType(typeof(Job), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> RunJobNowById(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobService.RunJobNowByIdAsync(id);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("DeactivateJob", Name = "DeactivateJob")]
|
||||||
|
[ProducesResponseType(typeof(Job), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> DeactivateJob([FromBody] Job job)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobService.DeactivateJobAsync(job);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("RescheduleJob", Name = "RescheduleJob")]
|
||||||
|
[ProducesResponseType(typeof(Job), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> RescheduleJob([FromBody] Job job)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobService.RescheduleJobAsync(job);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("RemoveSoftDeleteById", Name = "RemoveSoftDeleteById")]
|
||||||
|
[ProducesResponseType(typeof(Job), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> RemoveSoftDeleteById(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobService.RemoveSoftDeleteById(id);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete()]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Delete(int jobId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (jobId <= 0)
|
||||||
|
{
|
||||||
|
return BadRequest("Invalid Id");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _jobService.DeleteAsync(jobId);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("Copy")]
|
||||||
|
[ProducesResponseType(typeof(Job), 200)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Copy(CopyJobRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (request == null)
|
||||||
|
{
|
||||||
|
return BadRequest("Request cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
var job = await _jobService.CopyJobAsync(request);
|
||||||
|
return Ok(job);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[Route("[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class JobTagController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IJobTagService _jobTagService;
|
||||||
|
public JobTagController(IJobTagService jobTagService)
|
||||||
|
{
|
||||||
|
_jobTagService = jobTagService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<JobTag>), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Get()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobTagService.GetAsync();
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetByTagIdAsync")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<JobTag>), 200)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> GetByTagIdAsync(int tagId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (tagId <= 0)
|
||||||
|
{
|
||||||
|
return BadRequest("Invalid TagId.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _jobTagService.GetByTagIdAsync(tagId);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost()]
|
||||||
|
[ProducesResponseType(typeof(JobTag), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] JobTag request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobTagService.SaveAsync(request);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete()]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Delete(int jobTagId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (jobTagId <= 0)
|
||||||
|
{
|
||||||
|
return BadRequest("Invalid JobTagId");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _jobTagService.Delete(jobTagId);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Extensions;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class QueueCompletionController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IQueueCompletionService _queueCompletionService;
|
||||||
|
|
||||||
|
public QueueCompletionController(IQueueCompletionService completionService)
|
||||||
|
{
|
||||||
|
_queueCompletionService = completionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[ProducesResponseType(202)]
|
||||||
|
[ProducesResponseType(typeof(List<string>), 400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] DispatcherReturnResponse request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState.GetErrorMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
await _queueCompletionService.QueueCompletion(request);
|
||||||
|
return Accepted();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Extensions;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class QueueExecutionController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IQueueExecutionService _queueExecutionService;
|
||||||
|
|
||||||
|
public QueueExecutionController(IQueueExecutionService executionService)
|
||||||
|
{
|
||||||
|
_queueExecutionService = executionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("QueueRetryExecution")]
|
||||||
|
[ProducesResponseType(202)]
|
||||||
|
[ProducesResponseType(typeof(List<string>), 400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> QueueRetryExecutionController([FromBody] WorkItemExecution request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return BadRequest(ModelState.GetErrorMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
await _queueExecutionService.QueueRetryExecution(request);
|
||||||
|
return Accepted();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Models;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class StatusController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IEnvironmentView _environmentView;
|
||||||
|
|
||||||
|
public StatusController(IEnvironmentView environmentView)
|
||||||
|
{
|
||||||
|
_environmentView = environmentView;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet()]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public IActionResult Get()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Ok(_environmentView);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Failure to return status");
|
||||||
|
return StatusCode((int)System.Net.HttpStatusCode.InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[Route("[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class TagController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ITagService _tagService;
|
||||||
|
public TagController(ITagService tagService)
|
||||||
|
{
|
||||||
|
_tagService = tagService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<Tag>), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Get()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _tagService.GetAsync();
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetByIdAsync")]
|
||||||
|
[ProducesResponseType(typeof(Tag), 200)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> GetByIdAsync(int tagId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (tagId <= 0)
|
||||||
|
{
|
||||||
|
return BadRequest("Invalid TagId.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _tagService.GetByIdAsync(tagId);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ArgumentNullException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status404NotFound);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost()]
|
||||||
|
[ProducesResponseType(typeof(Tag), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Post([FromBody] Tag request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _tagService.SaveAsync(request);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut()]
|
||||||
|
[ProducesResponseType(typeof(Tag), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Put([FromBody] Tag request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _tagService.UpdateAsync(request);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete()]
|
||||||
|
[ProducesResponseType(200)]
|
||||||
|
[ProducesResponseType(400)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> Delete(int tagId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (tagId <= 0)
|
||||||
|
{
|
||||||
|
return BadRequest("Invalid TagId");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _tagService.DeleteAsync(tagId);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Controllers;
|
||||||
|
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class WorkItemController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IWorkItemService _workItemService;
|
||||||
|
|
||||||
|
public WorkItemController(IWorkItemService workItemService)
|
||||||
|
{
|
||||||
|
_workItemService = workItemService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("GetWorkItemsByJobId")]
|
||||||
|
[ProducesResponseType(typeof(IEnumerable<WorkItem>), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<IActionResult> GetWorkItemsByJobId(int jobId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _workItemService.GetByJobIdWithExecutionsAsync(jobId);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
catch (ServiceException)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Unexpected Exception");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
using LCU.Library.Internal.NotificationsModels.Models;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.DataAccess;
|
||||||
|
|
||||||
|
public interface INotificationsApiDao
|
||||||
|
{
|
||||||
|
Task SendEmailAsync(EmailRequest request);
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
using LCU.Api.Internal.DispatcherSharedLib.Configurations;
|
||||||
|
using LCU.Library.Internal.LcuHttpClient.HttpRequest;
|
||||||
|
using LCU.Library.Internal.NotificationsModels.Models;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.DataAccess;
|
||||||
|
|
||||||
|
public class NotificationsApiDao : INotificationsApiDao
|
||||||
|
{
|
||||||
|
private readonly NotificationApiConfiguration _configuration;
|
||||||
|
private readonly IPostRequest _postRequest;
|
||||||
|
|
||||||
|
public NotificationsApiDao(NotificationApiConfiguration configuration, IPostRequest postRequest)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_postRequest = postRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendEmailAsync(EmailRequest request)
|
||||||
|
{
|
||||||
|
await _postRequest.Async(new Uri(_configuration.EmailEndpointUrl), request);
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Extensions;
|
||||||
|
|
||||||
|
public static class ModelStateMessages
|
||||||
|
{
|
||||||
|
public static List<string> GetErrorMessages(this ModelStateDictionary dictionary)
|
||||||
|
{
|
||||||
|
return dictionary.SelectMany(m => m.Value.Errors)
|
||||||
|
.Select(m => m.ErrorMessage)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NCrontab;
|
||||||
|
using LCU.Api.Internal.DispatcherSharedLib.Utilities;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
using LCU.Library.Internal.LCUHostedService.LCUBackgroundService;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.HostedServices;
|
||||||
|
|
||||||
|
public class BatchErroredJobsHostedService : LCUBackgroundService, IBatchErroredJobsHostedService
|
||||||
|
{
|
||||||
|
private readonly ApiConfiguration _apiConfiguration;
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
private readonly CrontabSchedule _batchErroredJobsSchedule;
|
||||||
|
private DateTime _nextScheduleRun;
|
||||||
|
public const string DefaultBatchErroredJobsCronPattern = "*/15 * * * *";
|
||||||
|
private CancellationToken _cancellationToken;
|
||||||
|
|
||||||
|
public BatchErroredJobsHostedService(IServiceScopeFactory scopeFactory,
|
||||||
|
ApiConfiguration apiConfiguration)
|
||||||
|
{
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
_apiConfiguration = apiConfiguration;
|
||||||
|
_batchErroredJobsSchedule = CronHelpers.SetCronSchedule(_apiConfiguration.BatchErroredJobsCronSchedule,
|
||||||
|
DefaultBatchErroredJobsCronPattern);
|
||||||
|
_nextScheduleRun = _batchErroredJobsSchedule.GetNextOccurrence(DateTime.Now);
|
||||||
|
_cancellationToken = new CancellationToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!_apiConfiguration.FeatureFlags.RunBatchErroredJobsHostedService)
|
||||||
|
{
|
||||||
|
Log.Error("Batch Errored Jobs Hosted Service is disabled by feature flag. Service not started.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Debug("Dispatcher's Batch Errored Jobs Hosted Service is starting.");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await Task.Delay(UntilNextExecution(), cancellationToken); // wait until next time
|
||||||
|
|
||||||
|
using (var scope = _scopeFactory.CreateAsyncScope())
|
||||||
|
{
|
||||||
|
Log.Debug("Running Batch Errored Jobs Service");
|
||||||
|
await scope.ServiceProvider.GetRequiredService<IBatchErroredJobsService>().PerformBatchErroredJobs(_cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
_nextScheduleRun = _batchErroredJobsSchedule.GetNextOccurrence(DateTime.Now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(OperationCanceledException taskCanceledEx)
|
||||||
|
{
|
||||||
|
Log.Error(taskCanceledEx, $"Manual start/stop of Dispatcher's Batch Errored Jobs Hosted Service was initiated.");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception,
|
||||||
|
"Error trying to startup main thread for Dispatcher's Batch Errored Jobs Hosted Service");
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Debug("Dispatcher's Batch Errored Jobs Hosted Service is stopping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int UntilNextExecution() => Math.Max(0, (int)_nextScheduleRun.Subtract(DateTime.Now).TotalMilliseconds);
|
||||||
|
}
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
using LCU.Api.Internal.DispatcherSharedLib.Utilities;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NCrontab;
|
||||||
|
using System.Threading;
|
||||||
|
using LCU.Library.Internal.LCUHostedService.LCUBackgroundService;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.HostedServices;
|
||||||
|
|
||||||
|
public class CleanUpJobsHostedService : LCUBackgroundService, ICleanUpJobsHostedService
|
||||||
|
{
|
||||||
|
private readonly ApiConfiguration _apiConfiguration;
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
private readonly CrontabSchedule _cleanUpJobSchedule;
|
||||||
|
private DateTime _nextScheduleRun;
|
||||||
|
public const string DefaultCleanUpJobCronPattern = "* 22 * * *";
|
||||||
|
private CancellationToken _cancellationToken;
|
||||||
|
|
||||||
|
public CleanUpJobsHostedService(IServiceScopeFactory scopeFactory,
|
||||||
|
ApiConfiguration apiConfiguration)
|
||||||
|
{
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
_apiConfiguration = apiConfiguration;
|
||||||
|
_cleanUpJobSchedule = CronHelpers.SetCronSchedule(_apiConfiguration.CleanUpJobsCronSchedule,
|
||||||
|
DefaultCleanUpJobCronPattern);
|
||||||
|
_nextScheduleRun = _cleanUpJobSchedule.GetNextOccurrence(DateTime.Now);
|
||||||
|
_cancellationToken = new CancellationToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!_apiConfiguration.FeatureFlags.RunCleanUpJobsHostedService)
|
||||||
|
{
|
||||||
|
Log.Error("Clean Up Job Hosted Service is disabled by feature flag. Service not started.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Debug("Dispatcher's Clean Up Job Hosted Service is starting.");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await Task.Delay(UntilNextExecution(), cancellationToken); // wait until next time
|
||||||
|
|
||||||
|
using (var scope = _scopeFactory.CreateAsyncScope())
|
||||||
|
{
|
||||||
|
Log.Debug("Running Clean Up Job Service");
|
||||||
|
await scope.ServiceProvider.GetRequiredService<ICleanUpJobsService>().PerformCleanUpJobs(_cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
_nextScheduleRun = _cleanUpJobSchedule.GetNextOccurrence(DateTime.Now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(OperationCanceledException taskCanceledEx)
|
||||||
|
{
|
||||||
|
Log.Error(taskCanceledEx, $"Manual start/stop of Dispatcher's Clean Up Job Hosted Service was initiated.");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception,
|
||||||
|
"Error trying to startup main thread for Dispatcher's Clean Up Job Hosted Service");
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Debug("Dispatcher's Clean Up Job Hosted Service is stopping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int UntilNextExecution() => Math.Max(0, (int)_nextScheduleRun.Subtract(DateTime.Now).TotalMilliseconds);
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
using LCU.Library.Internal.LCUHostedService.LCUBackgroundService;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.HostedServices
|
||||||
|
{
|
||||||
|
public interface IBatchErroredJobsHostedService : ILCUBackgroundService
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
using LCU.Library.Internal.LCUHostedService.LCUBackgroundService;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.HostedServices
|
||||||
|
{
|
||||||
|
public interface ICleanUpJobsHostedService : ILCUBackgroundService
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
using LCU.Library.Internal.LCUHostedService.LCUBackgroundService;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.HostedServices
|
||||||
|
{
|
||||||
|
public interface IJobSchedulerHostedService : ILCUBackgroundService
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NCrontab;
|
||||||
|
using LCU.Api.Internal.DispatcherSharedLib.Utilities;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
using LCU.Library.Internal.LCUHostedService.LCUBackgroundService;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.HostedServices;
|
||||||
|
|
||||||
|
public class JobSchedulerHostedService : LCUBackgroundService, IJobSchedulerHostedService
|
||||||
|
{
|
||||||
|
private readonly ApiConfiguration _apiConfiguration;
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
private CancellationToken _cancellationToken;
|
||||||
|
|
||||||
|
public JobSchedulerHostedService(IServiceScopeFactory scopeFactory,
|
||||||
|
ApiConfiguration apiConfiguration)
|
||||||
|
{
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
_apiConfiguration = apiConfiguration;
|
||||||
|
_cancellationToken = new CancellationToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!_apiConfiguration.FeatureFlags.RunJobSchedulerHostedService)
|
||||||
|
{
|
||||||
|
Log.Error("Job Scheduler Hosted Service is disabled by feature flag. Service not started.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Debug("Dispatcher's Job Schedule Hosted Service is starting.");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await Task.Delay(_apiConfiguration.DefaultDelayMilliseconds, cancellationToken); // wait until next time
|
||||||
|
|
||||||
|
using (var scope = _scopeFactory.CreateAsyncScope())
|
||||||
|
{
|
||||||
|
Log.Debug("Running Job Schedule Service");
|
||||||
|
await scope.ServiceProvider.GetRequiredService<IJobScheduleService>().PerformSchedule(_cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(OperationCanceledException taskCanceledEx)
|
||||||
|
{
|
||||||
|
Log.Error(taskCanceledEx, $"Manual start/stop of Dispatcher's Job Scheduler Hosted Service was initiated.");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception,
|
||||||
|
"Error trying to startup main thread for Dispatcher's Job Schedule Hosted Service");
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Debug("Dispatcher's Job Schedule Hosted Service is stopping.");
|
||||||
|
}
|
||||||
|
}
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||||
|
<Version>2.8.1</Version>
|
||||||
|
<Description>
|
||||||
|
1.1.0 - Added NotificationsApi EmailRequest
|
||||||
|
1.2.0 - Added FileOperations CreateDirectory and RenameDirectory
|
||||||
|
1.3.0 - Added required end points and methods to handle Job Run now, Deactivation, Reschedule and Delete.
|
||||||
|
1.4.0 - Fix to always copy HTML Templates for Error Monitoring on build.
|
||||||
|
1.4.1 - Fix scheduler for Error Monitoring
|
||||||
|
1.5.0 - Addition of the JobErrorRecipient and functionality.
|
||||||
|
1.6.0 - Add Dynamic Zip and UnzipFileRequest
|
||||||
|
1.7.0 - Add ArchiveRequest
|
||||||
|
1.8.0 - Add Encrypt/Decrypt and SucceedOnError to Move/Copy/Rename/Delete File
|
||||||
|
1.9.0 - Upgrade to .net 6, upgrade to use new apiresponse, and some refactoring
|
||||||
|
2.0.0 - No longer Queueing Ingestion, removal of QueueLib, and clean up to be fully using MassTransit
|
||||||
|
2.1.0 - Added DocumentIngestor IngestorRequest
|
||||||
|
2.2.0 - Added DocumentIngestor IngestorPdfPartitionerRequest
|
||||||
|
2.3.0 - Added MemberDocumentNotificationRequest Workitem
|
||||||
|
2.4.0 - Added RemoteDepositCaptureXmlRequest Workitem and updated DocumentIngestor models
|
||||||
|
2.5.0 - Added DeleteDirectoryRequest Workitem and updated DocumentIngestor models
|
||||||
|
2.6.0 - Added UpdatePersonForCheckHoldRequest and DataCleanup Workitems
|
||||||
|
2.7.0 - Update FileOperations and FTP Api nuget for additions to SFTP upload and modify file
|
||||||
|
2.7.1 - Added HistoricalDocumentIngestor, HistoricalDocumentBatchIngestion, and HistoricalIngestorPdfPartitioner WorkItems
|
||||||
|
2.8.0 - Added ExtractRequest and ProcessingEmailRequest WorkItems from NoticeProcessor API
|
||||||
|
2.8.1 - Use new ActionBuilder and ActionService classes and other Refactors.
|
||||||
|
</Description>
|
||||||
|
<PackageReleaseNotes>
|
||||||
|
1.1.0 - Added NotificationsApi EmailRequest
|
||||||
|
1.2.0 - Added FileOperations CreateDirectory and RenameDirectory
|
||||||
|
1.3.0 - Added required end points and methods to handle Job Run now, Deactivation, Reschedule and Delete.
|
||||||
|
1.4.0 - Fix to always copy HTML Templates for Error Monitoring on build.
|
||||||
|
1.4.1 - Fix scheduler for Error Monitoring.
|
||||||
|
1.5.0 - Addition of the JobErrorRecipient and functionality.
|
||||||
|
1.6.0 - Add Dynamic Zip and UnzipFileRequest
|
||||||
|
1.7.0 - Add ArchiveRequest
|
||||||
|
1.8.0 - Add Encrypt/Decrypt and SucceedOnError to Move/Copy/Rename/Delete File
|
||||||
|
1.9.0 - Upgrade to .net 6, upgrade to use new apiresponse, and some refactoring
|
||||||
|
2.0.0 - No longer Queueing Ingestion, removal of QueueLib, and clean up to be fully using MassTransit
|
||||||
|
2.1.0 - Added DocumentIngestor IngestorRequest
|
||||||
|
2.2.0 - Added DocumentIngestor IngestorPdfPartitionerRequest
|
||||||
|
2.3.0 - Added MemberDocumentNotificationRequest Workitem
|
||||||
|
2.4.0 - Added RemoteDepositCaptureXmlRequest Workitem and updated DocumentIngestor models
|
||||||
|
2.5.0 - Added DeleteDirectoryRequest Workitem and updated DocumentIngestor models
|
||||||
|
2.6.0 - Added UpdatePersonForCheckHoldRequest and DataCleanup Workitems
|
||||||
|
2.7.0 - Update FileOperations and FTP Api nuget for additions to SFTP upload and modify file
|
||||||
|
2.7.1 - Added HistoricalDocumentIngestor, HistoricalDocumentBatchIngestion, and HistoricalIngestorPdfPartitioner WorkItems
|
||||||
|
2.8.0 - Added ExtractRequest and ProcessingEmailRequest WorkItems from NoticeProcessor API
|
||||||
|
2.8.1 - Use new ActionBuilder and ActionService classes and other Refactors.
|
||||||
|
</PackageReleaseNotes>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="LCU.Api.Internal.MaidBotLib" Version="1.0.2" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.ApiResponse" Version="3.4.1" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.ApiSecurity" Version="2.0.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.Configuration" Version="1.0.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.Exceptions" Version="1.0.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.LCUHostedService" Version="1.0.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.LcuHttpClient" Version="2.0.1" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.LcuMassTransit" Version="1.0.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.LcuMassTransitModels" Version="1.1.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.Logging" Version="2.2.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.RepoApiModels" Version="1.0.0" />
|
||||||
|
<PackageReference Include="LCU.Library.Internal.TypeExtensions" Version="1.2.2" />
|
||||||
|
<PackageReference Include="LCU.Shared.Collections.RepoApiLib" Version="2.4.5" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.5" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.3" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\LCU.Api.Internal.DispatcherLib\LCU.Api.Internal.DispatcherLib.csproj" />
|
||||||
|
<ProjectReference Include="..\LCU.Api.Internal.DispatcherSharedLib\LCU.Api.Internal.DispatcherSharedLib.csproj" />
|
||||||
|
<ProjectReference Include="..\LCU.Library.Internal.DispatcherModels\LCU.Library.Internal.DispatcherModels.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Properties\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Templates\BatchErroredJobsTemplate.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Templates\ErrorRowTemplate.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Models;
|
||||||
|
|
||||||
|
public class EnvironmentView : IEnvironmentView
|
||||||
|
{
|
||||||
|
public string Application => _env.ApplicationName;
|
||||||
|
public DateTime CurrentDate => DateTime.Now;
|
||||||
|
public string BuildVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||||
|
public DateTime BuildDate => AssemblyBuildDate();
|
||||||
|
public string Environment => _env.EnvironmentName;
|
||||||
|
private readonly IWebHostEnvironment _env;
|
||||||
|
|
||||||
|
public EnvironmentView(IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
_env = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime AssemblyBuildDate()
|
||||||
|
{
|
||||||
|
//https://stackoverflow.com/a/804310/266594
|
||||||
|
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
|
||||||
|
System.IO.FileInfo fileInfo = new System.IO.FileInfo(assembly.Location);
|
||||||
|
DateTime lastModified = fileInfo.LastWriteTime;
|
||||||
|
|
||||||
|
return lastModified;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Models;
|
||||||
|
|
||||||
|
public interface IEnvironmentView
|
||||||
|
{
|
||||||
|
string Application { get; }
|
||||||
|
DateTime CurrentDate { get; }
|
||||||
|
string BuildVersion { get; }
|
||||||
|
DateTime BuildDate { get; }
|
||||||
|
string Environment { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,396 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using MassTransit;
|
||||||
|
using LCU.Library.Internal.Configuration;
|
||||||
|
using LCU.Library.Internal.LcuMassTransit;
|
||||||
|
using LCU.Library.Internal.LcuMassTransitModels;
|
||||||
|
using LCU.Library.Internal.Logging;
|
||||||
|
using LCU.Library.Internal.Logging.Middleware;
|
||||||
|
using LCU.Library.Internal.Logging.Models;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Consumers;
|
||||||
|
using LCU.Api.Internal.Dispatcher.HostedServices;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Models;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
using LCU.Api.Internal.DispatcherLib.Persistence;
|
||||||
|
using LCU.Api.Internal.DispatcherLib.Repositories;
|
||||||
|
using LCU.Api.Internal.DispatcherSharedLib;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using LCU.Library.Internal.ApiSecurity.Constants;
|
||||||
|
using LCU.Library.Internal.ApiSecurity;
|
||||||
|
using LCU.Library.Internal.LcuHttpClient;
|
||||||
|
using LCU.Api.Internal.Dispatcher.DataAccess;
|
||||||
|
|
||||||
|
string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
|
||||||
|
ApiConfiguration _apiConfiguration;
|
||||||
|
|
||||||
|
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/workload-appsettings/appsettings.json", optional: true, reloadOnChange: true);
|
||||||
|
builder.Configuration.AddJsonFile("/vault/secrets/dispatcher-api-database-connection.json", optional: true, reloadOnChange: true);
|
||||||
|
builder.Configuration.AddJsonFile("/vault/secrets/dispatcher-api-seq-api-key.json", optional: true, reloadOnChange: true);
|
||||||
|
builder.Configuration.AddJsonFile("/vault/secrets/dispatcher-api-rabbitmq-username.json", optional: true, reloadOnChange: true);
|
||||||
|
builder.Configuration.AddJsonFile("/vault/secrets/dispatcher-api-rabbitmq-password.json", optional: true, reloadOnChange: true);
|
||||||
|
builder.Configuration.AddJsonFile("/vault/secrets/dispatcher-api-external-api-key-set.json", optional: true, reloadOnChange: true);
|
||||||
|
builder.Host.UseSerilog();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildServicesAndConfigurations(WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
var services = builder.Services;
|
||||||
|
var configuration = builder.Configuration;
|
||||||
|
services.AddControllers();
|
||||||
|
services.AddEndpointsApiExplorer();
|
||||||
|
services.AddSwaggerGen();
|
||||||
|
services.AddHttpClient();
|
||||||
|
|
||||||
|
_apiConfiguration = configuration.Bind<ApiConfiguration>(services, "Primary");
|
||||||
|
|
||||||
|
var connectionsConfiguration = configuration.Bind<ConnectionsConfiguration>(services);
|
||||||
|
|
||||||
|
var loggerConfiguration = new LoggingConfiguration(_apiConfiguration.AppName,
|
||||||
|
_apiConfiguration.SeqApiKey,
|
||||||
|
_apiConfiguration.SeqUrl);
|
||||||
|
var lcuLogging = new LcuLogging(loggerConfiguration);
|
||||||
|
lcuLogging.UseLcuLogging();
|
||||||
|
|
||||||
|
var connectionConfiguration = MassTransitConfigurationSetUp.AddConnectionConfiguration(ref services, configuration);
|
||||||
|
//debugging
|
||||||
|
Console.WriteLine(JsonConvert.SerializeObject(connectionConfiguration));
|
||||||
|
|
||||||
|
var receiveEndpointConfigurations = new ReceiveEndpointConfigurations()
|
||||||
|
{
|
||||||
|
ExecutionReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration,
|
||||||
|
"ExecutionReceiveEndpoint"),
|
||||||
|
CompletionReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration,
|
||||||
|
"CompletionReceiveEndpoint"),
|
||||||
|
DetermineOperationReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration,
|
||||||
|
"DetermineOperationReceiveEndpoint"),
|
||||||
|
JobFailureReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration,
|
||||||
|
"JobFailureReceiveEndpoint"),
|
||||||
|
SendErroredJobAlertReceiveEndpoint = MassTransitConfigurationSetUp.GetReceiveEndpointConfiguration(configuration,
|
||||||
|
"SendErroredJobAlertReceiveEndpoint")
|
||||||
|
};
|
||||||
|
services.AddSingleton(receiveEndpointConfigurations);
|
||||||
|
//debugging
|
||||||
|
Console.WriteLine(JsonConvert.SerializeObject(receiveEndpointConfigurations));
|
||||||
|
|
||||||
|
services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy(MyAllowSpecificOrigins,
|
||||||
|
builder =>
|
||||||
|
{
|
||||||
|
builder.WithOrigins(
|
||||||
|
_apiConfiguration.CorsAllowedOrigins
|
||||||
|
).AllowAnyHeader().AllowAnyMethod();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddDbContextPool<AppDbContext>(options =>
|
||||||
|
{
|
||||||
|
options.UseSqlServer(
|
||||||
|
connectionsConfiguration.Sql.DispatcherDb,
|
||||||
|
sqlServerOptionsAction: sqlOptions =>
|
||||||
|
{
|
||||||
|
sqlOptions.EnableRetryOnFailure(
|
||||||
|
maxRetryCount: connectionsConfiguration.Sql.SQLOptions.MaxRetryCount,
|
||||||
|
maxRetryDelay: connectionsConfiguration.Sql.SQLOptions.MaxRetryDelay,
|
||||||
|
errorNumbersToAdd: connectionsConfiguration.Sql.SQLOptions.ErrorNumbersToAdd
|
||||||
|
);
|
||||||
|
sqlOptions.CommandTimeout(connectionsConfiguration.Sql.SQLOptions.CommandTimeout);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
options.EnableDetailedErrors(connectionsConfiguration.Sql.SQLOptions.EnableDetailedErrors);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddMassTransitServices(ref services, connectionConfiguration, receiveEndpointConfigurations, _apiConfiguration);
|
||||||
|
services.AddDispatcherServices(configuration);
|
||||||
|
services.AddLCUHttpServices();
|
||||||
|
AddServices(services);
|
||||||
|
AddRepositories(services);
|
||||||
|
AddHostedServices(services);
|
||||||
|
|
||||||
|
services.AddSwaggerGen(c =>
|
||||||
|
{
|
||||||
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = $"{_apiConfiguration.AppName}", Version = "v1" });
|
||||||
|
|
||||||
|
//Add Security Entry Fields In Swagger
|
||||||
|
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference = new OpenApiReference
|
||||||
|
{
|
||||||
|
Id = HeaderKeys.ApiKey, // Must match the "AddSecurityDefinition"
|
||||||
|
Type = ReferenceType.SecurityScheme
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new List<string>()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference = new OpenApiReference
|
||||||
|
{
|
||||||
|
Id = HeaderKeys.Client, // Must match the "AddSecurityDefinition"
|
||||||
|
Type = ReferenceType.SecurityScheme
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new List<string>()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
c.AddSecurityDefinition(HeaderKeys.ApiKey,
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Description = HeaderKeys.ApiKey,
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Name = HeaderKeys.ApiKey,
|
||||||
|
Type = SecuritySchemeType.ApiKey,
|
||||||
|
Scheme = "apikey"
|
||||||
|
});
|
||||||
|
|
||||||
|
c.AddSecurityDefinition(HeaderKeys.Client,
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Description = HeaderKeys.Client,
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Name = HeaderKeys.Client,
|
||||||
|
Type = SecuritySchemeType.ApiKey,
|
||||||
|
Scheme = "apikey"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddApiKeySecurity(builder.Configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddMassTransitServices(ref IServiceCollection services,
|
||||||
|
ConnectionConfiguration connectionConfiguration,
|
||||||
|
ReceiveEndpointConfigurations receiveEndpointConfigurations,
|
||||||
|
ApiConfiguration apiConfiguration)
|
||||||
|
{
|
||||||
|
services.AddMassTransit(x =>
|
||||||
|
{
|
||||||
|
x.AddConsumer<ExecutionConsumer>();
|
||||||
|
x.AddConsumer<CompletionConsumer>();
|
||||||
|
x.AddConsumer<DetermineOperationConsumer>();
|
||||||
|
x.AddConsumer<JobFailureConsumer>();
|
||||||
|
x.AddConsumer<SendErroredJobAlertConsumer>();
|
||||||
|
|
||||||
|
x.UsingRabbitMq((context, cfg) =>
|
||||||
|
{
|
||||||
|
cfg.Host(connectionConfiguration.HostUrl, "/", host =>
|
||||||
|
{
|
||||||
|
host.Username(connectionConfiguration.UserName);
|
||||||
|
host.Password(connectionConfiguration.Password);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (apiConfiguration.FeatureFlags.RunExecutionConsumer)
|
||||||
|
{
|
||||||
|
cfg.ReceiveEndpoint(receiveEndpointConfigurations.ExecutionReceiveEndpoint.ReceiveEndpoint, e =>
|
||||||
|
{
|
||||||
|
e.ConfigureConsumer<ExecutionConsumer>(context);
|
||||||
|
e.SetQuorumQueue();
|
||||||
|
e.PrefetchCount = receiveEndpointConfigurations.CompletionReceiveEndpoint.PrefetchLimit;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiConfiguration.FeatureFlags.RunCompletionConsumer)
|
||||||
|
{
|
||||||
|
cfg.ReceiveEndpoint(receiveEndpointConfigurations.CompletionReceiveEndpoint.ReceiveEndpoint, e =>
|
||||||
|
{
|
||||||
|
e.ConfigureConsumer<CompletionConsumer>(context);
|
||||||
|
e.SetQuorumQueue();
|
||||||
|
e.PrefetchCount = receiveEndpointConfigurations.CompletionReceiveEndpoint.PrefetchLimit;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiConfiguration.FeatureFlags.RunDetermineOperationConsumer)
|
||||||
|
{
|
||||||
|
cfg.ReceiveEndpoint(receiveEndpointConfigurations.DetermineOperationReceiveEndpoint.ReceiveEndpoint, e =>
|
||||||
|
{
|
||||||
|
e.ConfigureConsumer<DetermineOperationConsumer>(context);
|
||||||
|
e.SetQuorumQueue();
|
||||||
|
e.PrefetchCount = receiveEndpointConfigurations.DetermineOperationReceiveEndpoint.PrefetchLimit;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiConfiguration.FeatureFlags.RunJobFailureConsumer)
|
||||||
|
{
|
||||||
|
cfg.ReceiveEndpoint(receiveEndpointConfigurations.JobFailureReceiveEndpoint.ReceiveEndpoint, e =>
|
||||||
|
{
|
||||||
|
e.ConfigureConsumer<JobFailureConsumer>(context);
|
||||||
|
e.SetQuorumQueue();
|
||||||
|
e.PrefetchCount = receiveEndpointConfigurations.JobFailureReceiveEndpoint.PrefetchLimit;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiConfiguration.FeatureFlags.RunSendErroredJobAlertConsumer)
|
||||||
|
{
|
||||||
|
cfg.ReceiveEndpoint(receiveEndpointConfigurations.SendErroredJobAlertReceiveEndpoint.ReceiveEndpoint, e =>
|
||||||
|
{
|
||||||
|
e.ConfigureConsumer<SendErroredJobAlertConsumer>(context, cons =>
|
||||||
|
{
|
||||||
|
cons.Options<BatchOptions>(options => options
|
||||||
|
.SetMessageLimit(200)
|
||||||
|
.SetTimeLimit(5000)
|
||||||
|
.SetConcurrencyLimit(1));
|
||||||
|
});
|
||||||
|
e.SetQuorumQueue();
|
||||||
|
e.PrefetchCount = receiveEndpointConfigurations.SendErroredJobAlertReceiveEndpoint.PrefetchLimit;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
services.AddMassTransitHostedService();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<IEnvironmentView, EnvironmentView>();
|
||||||
|
|
||||||
|
AddDispatcherOperationServices(services);
|
||||||
|
AddQueueServices(services);
|
||||||
|
services.AddScoped<IJobService, JobService>();
|
||||||
|
services.AddScoped<IJobTagService, JobTagService>();
|
||||||
|
services.AddScoped<ITagService, TagService>();
|
||||||
|
services.AddScoped<IWorkItemService, WorkItemService>();
|
||||||
|
services.AddScoped<IWorkItemExecutionService, WorkItemExecutionService>();
|
||||||
|
services.AddScoped<IExecutionMessageService, ExecutionMessageService>();
|
||||||
|
services.AddScoped<IJobErrorRecipientService, JobErrorRecipientService>();
|
||||||
|
services.AddScoped<INotificationService, NotificationService>();
|
||||||
|
services.AddScoped<ICleanUpJobSettingService, CleanUpJobSettingService>();
|
||||||
|
services.AddScoped<ICleanUpJobTagService, CleanUpJobTagService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddDispatcherOperationServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<IJobScheduleService, JobScheduleService>();
|
||||||
|
services.AddScoped<IBatchErroredJobsService, BatchErroredJobsService>();
|
||||||
|
services.AddScoped<IIngestionService, IngestionService>();
|
||||||
|
services.AddScoped<IExecutionService, ExecutionService>();
|
||||||
|
services.AddScoped<ICompletionService, CompletionService>();
|
||||||
|
|
||||||
|
services.AddScoped<ICleanUpJobsService, CleanUpJobsService>();
|
||||||
|
services.AddScoped<IDetermineOperationService, DetermineOperationService >();
|
||||||
|
services.AddScoped<IJobFailureService, JobFailureService >();
|
||||||
|
services.AddScoped<ISendErroredJobAlertService, SendErroredJobAlertService >();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddQueueServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<IQueueExecutionService, QueueExecutionService>();
|
||||||
|
services.AddScoped<IQueueCompletionService, QueueCompletionService>();
|
||||||
|
services.AddScoped<IQueueSendErroredJobAlertService, QueueSendErroredJobAlertService>();
|
||||||
|
services.AddScoped<IQueueDetermineOperationService, QueueDetermineOperationService>();
|
||||||
|
services.AddScoped<IQueueJobFailureService, QueueJobFailureService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddRepositories(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<IUnitOfWork, UnitOfWork>();
|
||||||
|
services.AddScoped<IJobRepository, JobRepository>();
|
||||||
|
services.AddScoped<IJobErrorRecipientRepository, JobErrorRecipientRepository>();
|
||||||
|
services.AddScoped<IJobTagRepository, JobTagRepository>();
|
||||||
|
services.AddScoped<ITagRepository, TagRepository>();
|
||||||
|
services.AddScoped<IWorkItemRepository, WorkItemRepository>();
|
||||||
|
services.AddScoped<IWorkItemExecutionRepository, WorkItemExecutionRepository>();
|
||||||
|
services.AddScoped<IExecutionMessageRepository, ExecutionMessageRepository>();
|
||||||
|
services.AddScoped<ICleanUpJobSettingRepository, CleanUpJobSettingRepository>();
|
||||||
|
services.AddScoped<ICleanUpJobTagRepository, CleanUpJobTagRepository>();
|
||||||
|
services.AddScoped<INotificationsApiDao, NotificationsApiDao>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddHostedServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton<ICleanUpJobsHostedService, CleanUpJobsHostedService>();
|
||||||
|
services.AddSingleton<IBatchErroredJobsHostedService, BatchErroredJobsHostedService>();
|
||||||
|
services.AddSingleton<IJobSchedulerHostedService, JobSchedulerHostedService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildAppAndRun(WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
if (_apiConfiguration.FeatureFlags.RunCleanUpJobsHostedService)
|
||||||
|
{
|
||||||
|
var service = app.Services.GetRequiredService<ICleanUpJobsHostedService>();
|
||||||
|
service.StartAsync().Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_apiConfiguration.FeatureFlags.RunJobSchedulerHostedService)
|
||||||
|
{
|
||||||
|
var service = app.Services.GetRequiredService<IJobSchedulerHostedService>();
|
||||||
|
service.StartAsync().Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_apiConfiguration.FeatureFlags.RunBatchErroredJobsHostedService)
|
||||||
|
{
|
||||||
|
var service = app.Services.GetRequiredService<IBatchErroredJobsHostedService>();
|
||||||
|
service.StartAsync().Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<OpenApiServer>()
|
||||||
|
{
|
||||||
|
new() { Description = serverUrl, Url = serverUrl }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.UseSwaggerUI(c =>
|
||||||
|
{
|
||||||
|
c.RoutePrefix = "swagger";
|
||||||
|
c.SwaggerEndpoint("v1/swagger.json", "LCU.Api.Internal.Dispatcher v1");
|
||||||
|
});
|
||||||
|
app.UseMiddleware<RequestResponseLoggingMiddleware>();
|
||||||
|
app.UseSerilogRequestLogging(options =>
|
||||||
|
{
|
||||||
|
options.EnrichDiagnosticContext = RequestResponseLoggingMiddleware.RequestDataParser.EnrichFromRequest;
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseCors(MyAllowSpecificOrigins);
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.MapControllers();
|
||||||
|
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
}
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
using LCU.Api.Internal.DispatcherLib.Repositories;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services
|
||||||
|
{
|
||||||
|
public class CleanUpJobSettingService : ICleanUpJobSettingService
|
||||||
|
{
|
||||||
|
private readonly ICleanUpJobSettingRepository _cleanUpJobSettingRepository;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
|
||||||
|
public CleanUpJobSettingService(IUnitOfWork unitOfWork,
|
||||||
|
ICleanUpJobSettingRepository cleanUpJobSettingRepository)
|
||||||
|
{
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
_cleanUpJobSettingRepository = cleanUpJobSettingRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<CleanUpJobSetting>> GetAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _cleanUpJobSettingRepository.ListAsync();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving CleanUpJobSetting.");
|
||||||
|
throw new ServiceException("Failure retrieving CleanUpJobSetting.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CleanUpJobSetting> GetByCleanUpJobSettingIdAsync(int cleanUpJobSettingId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _cleanUpJobSettingRepository.FindByCleanUpJobSettingIdAsync(cleanUpJobSettingId);
|
||||||
|
}
|
||||||
|
catch (ArgumentNullException exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure getting dispatcher Clean Up Job Settings by Id. CleanUpJobSettingId was null");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving dispatcher Clean Up Job Settings. CleanUpJobSettingId: {@cleanUpJobSettingId}", cleanUpJobSettingId);
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher Clean Up Job Settings. CleanUpJobSettingId: {cleanUpJobSettingId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CleanUpJobSetting> SaveAsync(CleanUpJobSetting request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _cleanUpJobSettingRepository.AddAsync(request);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure saving dispatcher Clean Up Job Settings. CleanUpJobSettingId: {@request.CleanUpJobSettingId}", request.CleanUpJobSettingId);
|
||||||
|
throw new ServiceException($"Failure saving dispatcher Clean Up Job Settings. CleanUpJobSettingId: {request.CleanUpJobSettingId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CleanUpJobSetting> UpdateAsync(CleanUpJobSetting request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var existing = await GetByCleanUpJobSettingIdAsync(request.CleanUpJobSettingId);
|
||||||
|
if(existing == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
existing.CompletionDaysPast = request.CompletionDaysPast;
|
||||||
|
_cleanUpJobSettingRepository.Update(existing);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, "SQL Failure updating dispatcher Clean Up Job Settings. CleanUpJobSettingId: {@request.CleanUpJobSettingId}", request.CleanUpJobSettingId);
|
||||||
|
throw new ServiceException($"SQL Failure updating dispatcher Clean Up Job Settings. CleanUpJobSettingId: {request.CleanUpJobSettingId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "SQL Failure updating dispatcher Clean Up Job Settings. CleanUpJobSettingId: {@request.CleanUpJobSettingId}", request.CleanUpJobSettingId);
|
||||||
|
throw new ServiceException($"SQL Failure updating dispatcher Clean Up Job Settings. CleanUpJobSettingId: {request.CleanUpJobSettingId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(int cleanUpJobSettingId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cleanUpJobSetting = await _cleanUpJobSettingRepository.FindByCleanUpJobSettingIdAsync(cleanUpJobSettingId);
|
||||||
|
ArgumentNullException.ThrowIfNull(cleanUpJobSetting);
|
||||||
|
_cleanUpJobSettingRepository.Remove(cleanUpJobSetting);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, "SQL Failure removing dispatcher Clean Up Job Settings. CleanUpJobSettingId: {@cleanUpJobSettingId}", cleanUpJobSettingId);
|
||||||
|
throw new ServiceException($"SQL Failure removing dispatcher Clean Up Job Settings. CleanUpJobSettingId: {cleanUpJobSettingId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure removing dispatcher Clean Up Job Settings. CleanUpJobSettingId: {@cleanUpJobSettingId}", cleanUpJobSettingId);
|
||||||
|
throw new ServiceException($"Failure removing dispatcher Clean Up Job Settings. CleanUpJobSettingId: {cleanUpJobSettingId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+135
@@ -0,0 +1,135 @@
|
|||||||
|
using LCU.Api.Internal.DispatcherLib.Repositories;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public class CleanUpJobTagService : ICleanUpJobTagService
|
||||||
|
{
|
||||||
|
private readonly ICleanUpJobTagRepository _cleanUpJobTagRepository;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
|
||||||
|
public CleanUpJobTagService(ICleanUpJobTagRepository cleanUpJobRepository,
|
||||||
|
IUnitOfWork unitOfWork)
|
||||||
|
{
|
||||||
|
_cleanUpJobTagRepository = cleanUpJobRepository;
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<CleanUpJobTag>> GetAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _cleanUpJobTagRepository.ListAsync();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving CleanUpJobag.");
|
||||||
|
throw new ServiceException("Failure retrieving CleanUpJobTag.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CleanUpJobTag> GetByCleanUpJobTagIdAsync(int cleanUpJobTagId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(cleanUpJobTagId);
|
||||||
|
return await _cleanUpJobTagRepository.FindByCleanUpJobTagIdAsync(cleanUpJobTagId);
|
||||||
|
}
|
||||||
|
catch (ArgumentNullException exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure getting dispatcher Clean Up Job Tag by Id. CleanUpJobTagId was null");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving dispatcher Clean Up Job Tag. CleanUpJobTagId: {@cleanUpJobTagId}", cleanUpJobTagId);
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher Clean Up Job Tag. CleanUpJobTagId: {cleanUpJobTagId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<CleanUpJobTag>> GetByCleanUpJobSettingIdAsync(int cleanUpJobSettingId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(cleanUpJobSettingId);
|
||||||
|
return await _cleanUpJobTagRepository.FindByCleanUpJobSettingIdAsync(cleanUpJobSettingId);
|
||||||
|
}
|
||||||
|
catch (ArgumentNullException exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure getting dispatcher Clean Up Job Tags bySettings Id. CleanUpJobSettingId was null");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving dispatcher Clean Up Job Tag. CleanUpJobSettingId: {@cleanUpJobSettingId}", cleanUpJobSettingId);
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher Clean Up Job Tag. CleanUpJobSettingId: {cleanUpJobSettingId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CleanUpJobTag> SaveAsync(CleanUpJobTag request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _cleanUpJobTagRepository.AddAsync(request);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure saving dispatcher Clean Up Job Tag. CleanUpJobTagId: {@request.CleanUpJobTagId}", request.CleanUpJobTagId);
|
||||||
|
throw new ServiceException($"Failure saving dispatcher Clean Up Job Tag. CleanUpJobTagId: {request.CleanUpJobTagId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(int cleanUpJobTagId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cleanUpJobTag = await _cleanUpJobTagRepository.FindByCleanUpJobTagIdAsync(cleanUpJobTagId);
|
||||||
|
ArgumentNullException.ThrowIfNull(cleanUpJobTag);
|
||||||
|
_cleanUpJobTagRepository.Remove(cleanUpJobTag);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, "SQL Failure removing dispatcher Clean Up Job Tag. CleanUpJobTagId: {@cleanUpJobTagId}", cleanUpJobTagId);
|
||||||
|
throw new ServiceException($"SQL Failure removing dispatcher Clean Up Job Tag. CleanUpJobTagId: {cleanUpJobTagId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure removing dispatcher Clean Up Job Tag. CleanUpJobTagId: {@cleanUpJobTagId}", cleanUpJobTagId);
|
||||||
|
throw new ServiceException($"Failure removing dispatcher Clean Up Job Tag. CleanUpJobTagId: {cleanUpJobTagId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteByCleanUpJobSettingIdAsync(int cleanUpJobSettingId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cleanUpJobTags = await _cleanUpJobTagRepository.FindByCleanUpJobSettingIdAsync(cleanUpJobSettingId);
|
||||||
|
|
||||||
|
if(!cleanUpJobTags.Any())
|
||||||
|
{
|
||||||
|
throw new Exception($"No CleanUpJobTags found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var tag in cleanUpJobTags)
|
||||||
|
{
|
||||||
|
_cleanUpJobTagRepository.Remove(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, "SQL Failure removing dispatcher Clean Up Job Tag. CleanUpJobSettingId: {@cleanUpJobSettingId}", cleanUpJobSettingId);
|
||||||
|
throw new ServiceException($"SQL Failure removing dispatcher Clean Up Job Tag. CleanUpJobSettingId: {cleanUpJobSettingId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure removing dispatcher Clean Up Job Tag. CleanUpJobSettingId: {@cleanUpJobSettingId}", cleanUpJobSettingId);
|
||||||
|
throw new ServiceException($"Failure removing dispatcher Clean Up Job Tag. CleanUpJobSettingId: {cleanUpJobSettingId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
|
||||||
|
public class BatchErroredJobsService : IBatchErroredJobsService
|
||||||
|
{
|
||||||
|
private readonly IQueueSendErroredJobAlertService _queueSendErroredJobAlertService;
|
||||||
|
private readonly IJobService _jobService;
|
||||||
|
|
||||||
|
public BatchErroredJobsService(IJobService jobService,
|
||||||
|
IQueueSendErroredJobAlertService queueSendErroredJobAlertService)
|
||||||
|
{
|
||||||
|
_jobService = jobService;
|
||||||
|
_queueSendErroredJobAlertService = queueSendErroredJobAlertService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PerformBatchErroredJobs(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jobs = await _jobService.GetErrorMonitoringJobs();
|
||||||
|
if (jobs == null || !jobs.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var batchedErroredJobAlertRequest = CreateBatchedErroredJobAlerts(jobs);
|
||||||
|
|
||||||
|
await _queueSendErroredJobAlertService.QueueSendErroredJobAlert(batchedErroredJobAlertRequest);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
//Fatal as we need to know if we can't get out our errors
|
||||||
|
Log.ForContext(nameof(PropertyEnum.Group),
|
||||||
|
nameof(GroupEnum.ITDevelopment))
|
||||||
|
.Fatal(ex, $"Failed to perform Error Monitoring.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BatchedErroredJobAlertRequest CreateBatchedErroredJobAlerts(IEnumerable<Job> jobs)
|
||||||
|
{
|
||||||
|
var batchedErroredJobAlertRequests = new BatchedErroredJobAlertRequest()
|
||||||
|
{
|
||||||
|
ErroredJobAlertRequests = new List<ErroredJobAlertRequest>()
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var job in jobs)
|
||||||
|
{
|
||||||
|
var failedOrProcessingWorkItem = job.WorkItems
|
||||||
|
.FirstOrDefault(wi => wi.WorkItemExecutions != null &&
|
||||||
|
wi.WorkItemExecutions.Any(wie => wie.Status == StatusType.Failed || wie.Status == StatusType.Processing));
|
||||||
|
|
||||||
|
if (failedOrProcessingWorkItem == null || failedOrProcessingWorkItem.CurrentWorkItemExecution == null)
|
||||||
|
{
|
||||||
|
Log.Warning($"Failed or Processing workItem not found during CreateBatchedErroredJobAlerts. JobId and Name: {job.JobId}: {job.Name}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
batchedErroredJobAlertRequests.ErroredJobAlertRequests.Add(new ErroredJobAlertRequest
|
||||||
|
{
|
||||||
|
CurrentJob = job,
|
||||||
|
CurrentWorkItem = failedOrProcessingWorkItem
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return batchedErroredJobAlertRequests;
|
||||||
|
}
|
||||||
|
}
|
||||||
+172
@@ -0,0 +1,172 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
using LCU.Api.Internal.DispatcherLib.Repositories;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Results;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
|
||||||
|
public class CleanUpJobsService : ICleanUpJobsService
|
||||||
|
{
|
||||||
|
private readonly IJobService _jobService;
|
||||||
|
private readonly ICleanUpJobSettingService _cleanUpJobSettingService;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
private readonly ApiConfiguration _apiConfiguration;
|
||||||
|
|
||||||
|
public CleanUpJobsService(IJobService jobService,
|
||||||
|
ICleanUpJobSettingService cleanUpJobSettingService,
|
||||||
|
IUnitOfWork unitOfWork,
|
||||||
|
ApiConfiguration apiConfiguration)
|
||||||
|
{
|
||||||
|
_jobService = jobService;
|
||||||
|
_cleanUpJobSettingService = cleanUpJobSettingService;
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
_apiConfiguration = apiConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PerformCleanUpJobs(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = new CleanUpJobBatchResult();
|
||||||
|
|
||||||
|
result = await PerformSoftCleanUpJobTask(result);
|
||||||
|
result = await PerformHardDeleteCleanUpJobTask(result);
|
||||||
|
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
Log.ForContext(nameof(PropertyEnum.Group),
|
||||||
|
nameof(GroupEnum.ITDevelopment))
|
||||||
|
.Error("CleanUpJob Batch Result: {@CleanUpJobBatchResult}", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.ForContext(nameof(PropertyEnum.Group),
|
||||||
|
nameof(GroupEnum.ITDevelopment))
|
||||||
|
.Error(ex, $"Failed to perform Clean Up Jobs.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CleanUpJobBatchResult> PerformSoftCleanUpJobTask(CleanUpJobBatchResult result)
|
||||||
|
{
|
||||||
|
var jobs = await _jobService.GetInactiveExcludeSoftDeletedTrackedAsync();
|
||||||
|
|
||||||
|
var cleanUpJobSettings = await _cleanUpJobSettingService.GetAsync();
|
||||||
|
|
||||||
|
foreach (var setting in cleanUpJobSettings)
|
||||||
|
{
|
||||||
|
if (setting.CleanUpJobTags == null || !setting.CleanUpJobTags.Any())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var job in jobs)
|
||||||
|
{
|
||||||
|
result = PerformCleanUpTaskForJob(result, job, setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ValidateShouldSoftDelete(CleanUpJobSetting setting, Job job, CleanUpJobBatchResult result)
|
||||||
|
{
|
||||||
|
var shouldSoftDelete = ShouldSoftDeleteByTag(setting, job);
|
||||||
|
|
||||||
|
if (!shouldSoftDelete)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShouldSoftDeleteByDate(setting, job, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShouldSoftDeleteByDate(CleanUpJobSetting setting, Job job, CleanUpJobBatchResult result)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!job.WorkItems.Any() || !job.WorkItems.FirstOrDefault().WorkItemExecutions.Any())
|
||||||
|
{
|
||||||
|
result.AddToFailureCount($"Job would be cleaned but cannot determine if it is old enough to be cleaned due to not having any workItemExecutions. Job: {job.JobId} - {job.Name}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentDate = DateTime.Now;
|
||||||
|
var cutoffDate = currentDate.AddDays(setting.CompletionDaysPast);
|
||||||
|
var jobLastCompletedTime = job.WorkItems.FirstOrDefault()
|
||||||
|
.WorkItemExecutions.OrderByDescending(wie => wie.CompletedTimestamp).FirstOrDefault().CompletedTimestamp.Value;
|
||||||
|
|
||||||
|
return DateTime.Compare(cutoffDate, jobLastCompletedTime) > 0;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var message = $"Error while attempting to compare CompletedTimestamp for SoftDeletion. Job: {job.JobId} - {job.Name}. ExeptionMessage: {ex.Message}";
|
||||||
|
Log.Error(message);
|
||||||
|
throw new Exception(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShouldSoftDeleteByTag(CleanUpJobSetting setting, Job job)
|
||||||
|
{
|
||||||
|
if (job.JobTags.Count != setting.CleanUpJobTags.Count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var orderedJobTagIds = job.JobTags.Select(t => t.TagId).OrderBy(t => t).ToList();
|
||||||
|
var orderedCleanUpJobTagIds = setting.CleanUpJobTags.Select(t => t.TagId).OrderBy(t => t).ToList();
|
||||||
|
|
||||||
|
return orderedJobTagIds.SequenceEqual(orderedCleanUpJobTagIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CleanUpJobBatchResult> PerformHardDeleteCleanUpJobTask(CleanUpJobBatchResult result)
|
||||||
|
{
|
||||||
|
var softDeletedJobs = await _jobService.GetSoftDeletedTrackedAsync(DateTime.Now.AddDays(_apiConfiguration.CleanUpJobsDaysToDelete));
|
||||||
|
foreach (var job in softDeletedJobs)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _jobService.DeleteFromCleanupAsync(job);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex.Message, "Failure to hard delete dispatcher job through Clean Up Job Task. Job: {@Job}", job);
|
||||||
|
result.AddToFailureCount(ex.Message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CleanUpJobBatchResult PerformCleanUpTaskForJob(CleanUpJobBatchResult result, Job job, CleanUpJobSetting setting)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var shouldSoftDelete = ValidateShouldSoftDelete(setting, job, result);
|
||||||
|
|
||||||
|
if (!shouldSoftDelete)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
job.SoftDeleteDate = DateTime.Now;
|
||||||
|
|
||||||
|
var updatedJob = _jobService.UpdateExcludingWorkItemsAsyncWithoutSave(job);
|
||||||
|
if (updatedJob == null || updatedJob.Result.JobId != job.JobId)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failure to setup soft delete update for job. Job:{JsonConvert.SerializeObject(job)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
result.AddToFailureCount(ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using LCU.Library.Internal.ApiResponse.Models;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
|
||||||
|
public class CompletionService : ICompletionService
|
||||||
|
{
|
||||||
|
private readonly IJobService _jobService;
|
||||||
|
private readonly IWorkItemService _workItemService;
|
||||||
|
private readonly IExecutionMessageService _executionMessageService;
|
||||||
|
private readonly IQueueJobFailureService _queueJobFailureService;
|
||||||
|
private readonly IQueueDetermineOperationService _queueDetermineOperationService;
|
||||||
|
|
||||||
|
public CompletionService(IJobService jobService,
|
||||||
|
IWorkItemService workItemService,
|
||||||
|
IExecutionMessageService executionMessageService,
|
||||||
|
IQueueJobFailureService queueJobFailureService,
|
||||||
|
IQueueDetermineOperationService queueDetermineOperationService)
|
||||||
|
{
|
||||||
|
_jobService = jobService;
|
||||||
|
_workItemService = workItemService;
|
||||||
|
_executionMessageService = executionMessageService;
|
||||||
|
_queueJobFailureService = queueJobFailureService;
|
||||||
|
_queueDetermineOperationService = queueDetermineOperationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleCompletionRequest(Response response)
|
||||||
|
{
|
||||||
|
var dispatcherReturnResponse = JsonConvert.DeserializeObject<DispatcherReturnResponse>(response.ResponsePayload);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(dispatcherReturnResponse);
|
||||||
|
|
||||||
|
var job = await _jobService.GetByWorkItemIdExcludingWorkItemsAsync(dispatcherReturnResponse.WorkItemId);
|
||||||
|
|
||||||
|
if (!response.Successful)
|
||||||
|
{
|
||||||
|
await _queueJobFailureService.QueueJobFailure(job.JobId,
|
||||||
|
dispatcherReturnResponse.WorkItemId,
|
||||||
|
response.ResponseStack == null || response.ResponseStack.Any() ? response.ResponseStack : new List<string>());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var workItem = await _workItemService.GetByIdAsync(dispatcherReturnResponse.WorkItemId);
|
||||||
|
if (workItem.CurrentWorkItemExecution == null)
|
||||||
|
{
|
||||||
|
Log.Warning($"CurrentWorkitemExecution was null for WorkItemId:{dispatcherReturnResponse.WorkItemId}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await _executionMessageService.SaveExecutionMessages(workItem.CurrentWorkItemExecution.WorkItemExecutionId, response.ResponseStack);
|
||||||
|
|
||||||
|
if (workItem.CanceledJobCounter > 0)
|
||||||
|
{
|
||||||
|
workItem = await _workItemService.ResetCanceledJobCounter(workItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
job.JobStatus = JobStatus.DeterminingOperation;
|
||||||
|
var updatedJob = await _jobService.UpdateAsync(job);
|
||||||
|
|
||||||
|
await _queueDetermineOperationService.QueueDetermineOperation(updatedJob, workItem, SetDetermineOperationType(dispatcherReturnResponse.Skipped));
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
var exceptionMessage = $"Failed to perform completion. Job may be stuck in an inoperable status. WorkItemId: {dispatcherReturnResponse.WorkItemId}";
|
||||||
|
Log.Error(exception, exceptionMessage);
|
||||||
|
throw new ServiceException(exceptionMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DetermineOperationType SetDetermineOperationType(bool isSkipped)
|
||||||
|
{
|
||||||
|
if(isSkipped)
|
||||||
|
{
|
||||||
|
return DetermineOperationType.Skipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DetermineOperationType.Completed;
|
||||||
|
}
|
||||||
|
}
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Entities;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices
|
||||||
|
{
|
||||||
|
public class DetermineOperationService : IDetermineOperationService
|
||||||
|
{
|
||||||
|
private readonly IJobService _jobService;
|
||||||
|
private readonly IWorkItemService _workItemService;
|
||||||
|
private readonly IWorkItemExecutionService _workItemExecutionService;
|
||||||
|
private readonly IQueueExecutionService _queueExecutionService;
|
||||||
|
|
||||||
|
public DetermineOperationService(IJobService jobService,
|
||||||
|
IWorkItemService workItemService,
|
||||||
|
IWorkItemExecutionService workItemExecutionService,
|
||||||
|
IQueueExecutionService queueExecutionService)
|
||||||
|
{
|
||||||
|
_jobService = jobService;
|
||||||
|
_workItemService = workItemService;
|
||||||
|
_workItemExecutionService = workItemExecutionService;
|
||||||
|
_queueExecutionService = queueExecutionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleDetermineOperationRequest(DetermineOperationRequest determineOperationRequest)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (determineOperationRequest.DetermineOperationType)
|
||||||
|
{
|
||||||
|
case DetermineOperationType.Failed:
|
||||||
|
await HandleFailedWorkItem(determineOperationRequest);
|
||||||
|
break;
|
||||||
|
case DetermineOperationType.Skipped:
|
||||||
|
await HandleWorkItemCompletion(determineOperationRequest, StatusType.Skipped);
|
||||||
|
break;
|
||||||
|
case DetermineOperationType.Completed:
|
||||||
|
await HandleWorkItemCompletion(determineOperationRequest, StatusType.Completed);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (ServiceException serviceException)
|
||||||
|
{
|
||||||
|
Log.Error(serviceException, $"Dispatcher Determine Operation Failure. " +
|
||||||
|
$"The Dispatcher Determine Operation is placed on LCU.Dispatcher.DetermineOperation_error.");
|
||||||
|
throw new ServiceException($"Failed to handle DetermineOperationRequest.");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failed to handle DetermineOperationRequest.");
|
||||||
|
throw new ServiceException($"Failed to handle DetermineOperationRequest.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleFailedWorkItem(DetermineOperationRequest determineOperationRequest)
|
||||||
|
{
|
||||||
|
await _jobService.CompleteJobAndWorkItem(determineOperationRequest.CurrentJob, determineOperationRequest.CurrentWorkItem.CurrentWorkItemExecution, StatusType.Failed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleWorkItemCompletion(DetermineOperationRequest determineOperationRequest, StatusType status)
|
||||||
|
{
|
||||||
|
var nextWorkItem = await _workItemService.GetNextWorkItemByJobId(determineOperationRequest.CurrentJob.JobId, determineOperationRequest.CurrentWorkItem.Order);
|
||||||
|
if (nextWorkItem == null)
|
||||||
|
{
|
||||||
|
await _jobService.CompleteJobAndWorkItem(determineOperationRequest.CurrentJob, determineOperationRequest.CurrentWorkItem.CurrentWorkItemExecution, status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _jobService.CompleteWorkItemAndContinueProcessingJob(determineOperationRequest.CurrentJob,
|
||||||
|
determineOperationRequest.CurrentWorkItem.CurrentWorkItemExecution,
|
||||||
|
status);
|
||||||
|
var newWorkItemExecution = await _workItemExecutionService.CreateWorkItemExecution(nextWorkItem.WorkItemId);
|
||||||
|
|
||||||
|
await _queueExecutionService.QueueExecution(newWorkItemExecution);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using LCU.Library.Internal.LcuHttpClient.HttpRequest;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
public class ExecutionService : IExecutionService
|
||||||
|
{
|
||||||
|
private readonly IPostRequest _postRequest;
|
||||||
|
private readonly IWorkItemService _workItemService;
|
||||||
|
private readonly IJobService _jobService;
|
||||||
|
private readonly IQueueCompletionService _queueCompletionService;
|
||||||
|
|
||||||
|
private const string HttRequestIsNullMessage = "Http Request is null";
|
||||||
|
|
||||||
|
public ExecutionService(IPostRequest postRequest,
|
||||||
|
IWorkItemService workItemService,
|
||||||
|
|
||||||
|
IJobService jobService,
|
||||||
|
IQueueCompletionService queueCompletionService)
|
||||||
|
{
|
||||||
|
_postRequest = postRequest;
|
||||||
|
_workItemService = workItemService;
|
||||||
|
_jobService = jobService;
|
||||||
|
_queueCompletionService = queueCompletionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleExecutionRequest(WorkItemExecution request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var executionResponse = await ExecuteRequest(request);
|
||||||
|
if (!executionResponse.IsSuccess)
|
||||||
|
{
|
||||||
|
await _queueCompletionService.QueueCompletion(new DispatcherReturnResponse() { WorkItemId = executionResponse.WorkItemExecution.WorkItemId },
|
||||||
|
executionResponse.Messages, executionResponse.IsSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failed to perform execution. WorkItemId: {request.WorkItemId}");
|
||||||
|
throw new ServiceException($"Failed to perform execution. WorkItemId: {request.WorkItemId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ExecutionResponse> ExecuteRequest(WorkItemExecution request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(request);
|
||||||
|
|
||||||
|
var requestedWorkItem = await _workItemService.GetByIdAsync(request.WorkItemId);
|
||||||
|
|
||||||
|
await UpdateJobStatusToProcessing(requestedWorkItem);
|
||||||
|
|
||||||
|
var result = await _postRequest.AsyncWithNewtonSerializedNoThrow(new Uri(requestedWorkItem.Url), requestedWorkItem.SerializedPayload);
|
||||||
|
if (!result.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var content = await result.Content.ReadAsStringAsync();
|
||||||
|
return new ExecutionResponse(false, request, content);
|
||||||
|
}
|
||||||
|
return new ExecutionResponse(true, request);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var errorMessage = $"Exception during ExecuteRequest. Exception Message: {ex.Message}. Inner Exception: {ex.InnerException}";
|
||||||
|
Log.Error(errorMessage);
|
||||||
|
return new ExecutionResponse(false, request, errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateJobStatusToProcessing(WorkItem requestedWorkItem)
|
||||||
|
{
|
||||||
|
var job = await _jobService.GetByWorkItemIdExcludingWorkItemsAsync(requestedWorkItem.WorkItemId);
|
||||||
|
if (job.JobStatus == JobStatus.Processing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
job.JobStatus = JobStatus.Processing;
|
||||||
|
await _jobService.UpdateAsync(job);
|
||||||
|
Log.Information("Job Id {JobId} was saved with status {Status}", job.JobId, job.JobStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ExecutionResponse
|
||||||
|
{
|
||||||
|
public ExecutionResponse(bool isSuccess, WorkItemExecution workItemExecution, string messages = null)
|
||||||
|
{
|
||||||
|
IsSuccess = isSuccess;
|
||||||
|
WorkItemExecution = workItemExecution;
|
||||||
|
Messages = messages != null ? new List<string>() { messages } : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSuccess { get; set; }
|
||||||
|
public List<string> Messages { get; set; }
|
||||||
|
public WorkItemExecution WorkItemExecution { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
using System.Threading;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
|
||||||
|
public interface IBatchErroredJobsService
|
||||||
|
{
|
||||||
|
Task PerformBatchErroredJobs(CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
|
||||||
|
public interface ICleanUpJobsService
|
||||||
|
{
|
||||||
|
Task PerformCleanUpJobs(CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
using LCU.Library.Internal.ApiResponse.Models;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
|
||||||
|
public interface ICompletionService
|
||||||
|
{
|
||||||
|
Task HandleCompletionRequest(Response response);
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices
|
||||||
|
{
|
||||||
|
public interface IDetermineOperationService
|
||||||
|
{
|
||||||
|
Task HandleDetermineOperationRequest(DetermineOperationRequest determineOperationRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
|
||||||
|
public interface IExecutionService
|
||||||
|
{
|
||||||
|
Task HandleExecutionRequest(WorkItemExecution request);
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
|
||||||
|
public interface IIngestionService
|
||||||
|
{
|
||||||
|
Task HandleIngestionRequest(Job request);
|
||||||
|
Task<bool> PerformIngestion(Job queueRequest);
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices
|
||||||
|
{
|
||||||
|
public interface IJobFailureService
|
||||||
|
{
|
||||||
|
Task HandleJobFailureRequest(JobFailureRequest jobFailureRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
using System.Threading;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
|
||||||
|
public interface IJobScheduleService
|
||||||
|
{
|
||||||
|
Task PerformSchedule(CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices
|
||||||
|
{
|
||||||
|
public interface ISendErroredJobAlertService
|
||||||
|
{
|
||||||
|
Task HandleSendingErroredJobAlerts(List<BatchedErroredJobAlertRequest> batchedErroredJobAlertRequestList);
|
||||||
|
}
|
||||||
|
}
|
||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
|
||||||
|
public class IngestionService : IIngestionService
|
||||||
|
{
|
||||||
|
private readonly IJobService _jobService;
|
||||||
|
private readonly IWorkItemService _workItemService;
|
||||||
|
private readonly IJobErrorRecipientService _jobErrorRecipientService;
|
||||||
|
private readonly IJobTagService _jobTagService;
|
||||||
|
|
||||||
|
public IngestionService(IJobService jobService,
|
||||||
|
IWorkItemService workItemService,
|
||||||
|
IJobErrorRecipientService jobErrorRecipientService,
|
||||||
|
IJobTagService jobTagService)
|
||||||
|
{
|
||||||
|
_jobService = jobService;
|
||||||
|
_workItemService = workItemService;
|
||||||
|
_jobErrorRecipientService = jobErrorRecipientService;
|
||||||
|
_jobTagService = jobTagService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleIngestionRequest(Job request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(request);
|
||||||
|
var result = await PerformIngestion(request);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
throw new ServiceException("Failure to PerformIngestion.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failed to perform execution.");
|
||||||
|
throw new ServiceException($"Failed to perform ingestion.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> PerformIngestion(Job request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (request.IsNewJob)
|
||||||
|
{
|
||||||
|
return await SaveNewRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await UpdateRequest(request);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Information(ex, $"Failed to perform ingestion.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SaveNewRequest(Job request)
|
||||||
|
{
|
||||||
|
request = await _jobService.SaveAsync(request);
|
||||||
|
if (request == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedWorkItems = await _workItemService.PopulateActionFieldsAndUpdateList(request.WorkItems);
|
||||||
|
if (updatedWorkItems == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> UpdateRequest(Job request)
|
||||||
|
{
|
||||||
|
var existingJob = await _jobService.GetByIdAsync(request.JobId);
|
||||||
|
|
||||||
|
await UpdateRequestWorkItems(request, existingJob);
|
||||||
|
UpdateRequestErrorRecipients(request, existingJob);
|
||||||
|
await UpdateRequestJobTags(request, existingJob);
|
||||||
|
request.JobStatus = (request.NextRunTime == null && request.CronSchedule == null) ? JobStatus.Inactive : existingJob.JobStatus;
|
||||||
|
|
||||||
|
var updatedJob = await _jobService.UpdateAsync(request);
|
||||||
|
if (updatedJob == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateRequestWorkItems(Job request, Job existingJob)
|
||||||
|
{
|
||||||
|
var removedWorkItems = existingJob.WorkItems.Where(wi =>
|
||||||
|
!request.WorkItems.Any(newWI =>
|
||||||
|
newWI.WorkItemId == wi.WorkItemId)).ToList();
|
||||||
|
await _workItemService.DeleteList(removedWorkItems);
|
||||||
|
|
||||||
|
var newWorkItems = request.WorkItems.Where(wi => wi.IsNewWorkItem).ToList();
|
||||||
|
_workItemService.SaveList(newWorkItems);
|
||||||
|
|
||||||
|
var updatedWorkItems = await _workItemService.PopulateActionFieldsAndUpdateList(request.WorkItems);
|
||||||
|
if (updatedWorkItems == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"WorkItem Error while Populating Action Fields and updating.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateRequestErrorRecipients(Job request, Job existingJob)
|
||||||
|
{
|
||||||
|
var removedJobErrorRecipients = existingJob.JobErrorRecipients.Where(recipient =>
|
||||||
|
!request.JobErrorRecipients.Any(newRecipient =>
|
||||||
|
newRecipient.Recipient == recipient.Recipient)).ToList();
|
||||||
|
_jobErrorRecipientService.DeleteList(removedJobErrorRecipients);
|
||||||
|
|
||||||
|
var newJobErrorRecipients = new List<JobErrorRecipient>();
|
||||||
|
foreach (var recipient in request.JobErrorRecipients)
|
||||||
|
{
|
||||||
|
var jobErrorRecipient = existingJob.JobErrorRecipients.FirstOrDefault(existing =>
|
||||||
|
existing.Recipient == recipient.Recipient);
|
||||||
|
|
||||||
|
if (jobErrorRecipient == null)
|
||||||
|
{
|
||||||
|
newJobErrorRecipients.Add(recipient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_jobErrorRecipientService.SaveList(newJobErrorRecipients);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateRequestJobTags(Job request, Job existingJob)
|
||||||
|
{
|
||||||
|
var removedJobTags = existingJob.JobTags.Where(tag =>
|
||||||
|
!request.JobTags.Any(jobTags =>
|
||||||
|
jobTags.TagId == tag.TagId)).ToList();
|
||||||
|
|
||||||
|
foreach (var tag in removedJobTags)
|
||||||
|
{
|
||||||
|
await _jobTagService.Delete(tag.JobTagId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newJobTags = new List<JobTag>();
|
||||||
|
foreach (var tag in request.JobTags)
|
||||||
|
{
|
||||||
|
var existingTag = existingJob.JobTags.FirstOrDefault(existing =>
|
||||||
|
existing.TagId == tag.TagId);
|
||||||
|
|
||||||
|
if (existingTag == null)
|
||||||
|
{
|
||||||
|
newJobTags.Add(tag);
|
||||||
|
var result = await _jobTagService.SaveAsync(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Entities;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using LCU.Library.Internal.DocumentIngestorModels.Models.IndexFileDocuments;
|
||||||
|
using LCU.Library.Internal.Exceptions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices
|
||||||
|
{
|
||||||
|
public class JobFailureService : IJobFailureService
|
||||||
|
{
|
||||||
|
private readonly IWorkItemService _workItemService;
|
||||||
|
private readonly IWorkItemExecutionService _workItemExecutionService;
|
||||||
|
private readonly IJobService _jobService;
|
||||||
|
private readonly IExecutionMessageService _executionMessageService;
|
||||||
|
private readonly IQueueDetermineOperationService _queueDetermineOperationService;
|
||||||
|
private readonly IQueueSendErroredJobAlertService _queueSendErroredJobAlertService;
|
||||||
|
|
||||||
|
public JobFailureService(IWorkItemService workItemService,
|
||||||
|
IWorkItemExecutionService workItemExecutionService,
|
||||||
|
IJobService jobService,
|
||||||
|
IExecutionMessageService executionMessageService,
|
||||||
|
IQueueDetermineOperationService queueDetermineOperationService,
|
||||||
|
IQueueSendErroredJobAlertService queueSendErroredJobAlertService)
|
||||||
|
{
|
||||||
|
_workItemService = workItemService;
|
||||||
|
_workItemExecutionService = workItemExecutionService;
|
||||||
|
_jobService = jobService;
|
||||||
|
_executionMessageService = executionMessageService;
|
||||||
|
_queueDetermineOperationService = queueDetermineOperationService;
|
||||||
|
_queueSendErroredJobAlertService = queueSendErroredJobAlertService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleJobFailureRequest(JobFailureRequest jobFailureRequest)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var workItem = await _workItemService.GetByIdAsync(jobFailureRequest.WorkItemId);
|
||||||
|
|
||||||
|
var updatedWorkItemExecution = await _workItemExecutionService.UpdateFailedWorkItemExecution(workItem.CurrentWorkItemExecution);
|
||||||
|
|
||||||
|
if (workItem.CancelJobOnFailure)
|
||||||
|
{
|
||||||
|
workItem = await _workItemService.IncreaseCanceledJobCounter(workItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
var job = await _jobService.GetByWorkItemIdExcludingWorkItemsIncludeErrorRecipientsAsync(jobFailureRequest.WorkItemId);
|
||||||
|
|
||||||
|
if (workItem.RequiresJobCancellation)
|
||||||
|
{
|
||||||
|
var message = $"This Job will be canceled and if scheduleable, rescheduled. The " +
|
||||||
|
$"WorkItem's canceled Job counter is currently at {workItem.CanceledJobCounter} and has " +
|
||||||
|
$"not yet went over the cancel Job limit of {workItem.CancelJobLimit}.";
|
||||||
|
jobFailureRequest.ExecutionMessages.Add(message);
|
||||||
|
job.JobStatus = JobStatus.DeterminingOperation;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
job.JobStatus = JobStatus.Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _executionMessageService.SaveExecutionMessages(workItem.CurrentWorkItemExecution.WorkItemExecutionId, jobFailureRequest.ExecutionMessages);
|
||||||
|
var updatedJob = await _jobService.UpdateAsync(job);
|
||||||
|
|
||||||
|
if (updatedJob.JobStatus == JobStatus.DeterminingOperation)
|
||||||
|
{
|
||||||
|
await _queueDetermineOperationService.QueueDetermineOperation(updatedJob, workItem, DetermineOperationType.Failed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _queueSendErroredJobAlertService.QueueSendErroredJobAlert(updatedJob, workItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ServiceException serviceException)
|
||||||
|
{
|
||||||
|
Log.Error(serviceException, $"Dispatcher Job Failure Request Failure. " +
|
||||||
|
$"The Dispatcher Job Failure Request is placed on LCU.Dispatcher.JobFailure_error.");
|
||||||
|
throw new ServiceException($"Failed to handle JobFailureRequest.");
|
||||||
|
}
|
||||||
|
catch(Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failed to handle JobFailureRequest.");
|
||||||
|
throw new ServiceException($"Failed to handle JobFailureRequest.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using System;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices;
|
||||||
|
|
||||||
|
public class JobScheduleService : IJobScheduleService
|
||||||
|
{
|
||||||
|
private readonly IQueueExecutionService _queueExecutionService;
|
||||||
|
private readonly IJobService _jobService;
|
||||||
|
private readonly IWorkItemService _workItemService;
|
||||||
|
private readonly IWorkItemExecutionService _workItemExecutionService;
|
||||||
|
|
||||||
|
public JobScheduleService(IQueueExecutionService queueExecutionService,
|
||||||
|
IJobService jobService,
|
||||||
|
IWorkItemService workItemService,
|
||||||
|
IWorkItemExecutionService workItemExecutionService)
|
||||||
|
{
|
||||||
|
_queueExecutionService = queueExecutionService;
|
||||||
|
_jobService = jobService;
|
||||||
|
_workItemService = workItemService;
|
||||||
|
_workItemExecutionService = workItemExecutionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PerformSchedule(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var readyToScheduleJobs = await _jobService.GetJobsReadyToBeScheduled();
|
||||||
|
|
||||||
|
foreach (var job in readyToScheduleJobs)
|
||||||
|
{
|
||||||
|
var workItem = await _workItemService.GetFirstOrDefaultWorkItemByJobId(job.JobId);
|
||||||
|
|
||||||
|
if (workItem == null)
|
||||||
|
{
|
||||||
|
// THIS SHOULD NEVER HAPPEN
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Error($"Failure in Dispatcher Job Scheduler. Attempting to schedule Job: {job.JobId} - {job.Name} and NO workItems were found to process.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var workItemExecution = await _workItemExecutionService.CreateWorkItemExecution(workItem.WorkItemId);
|
||||||
|
|
||||||
|
job.JobStatus = JobStatus.PendingToRun;
|
||||||
|
|
||||||
|
if (job.SoftDeleteDate != null)
|
||||||
|
{
|
||||||
|
job.SoftDeleteDate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _jobService.UpdateAsync(job);
|
||||||
|
Log.Information("Job Id {JobId} was saved with status {Status}", job.JobId, job.JobStatus);
|
||||||
|
|
||||||
|
await _queueExecutionService.QueueExecution(workItemExecution);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Error(exception, $"Failed to perform schedule.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.DispatcherOperationServices
|
||||||
|
{
|
||||||
|
public class SendErroredJobAlertService : ISendErroredJobAlertService
|
||||||
|
{
|
||||||
|
private readonly ApiConfiguration _apiConfiguration;
|
||||||
|
private readonly INotificationService _notificationService;
|
||||||
|
|
||||||
|
public SendErroredJobAlertService(ApiConfiguration apiConfiguration, INotificationService notificationService)
|
||||||
|
{
|
||||||
|
_apiConfiguration = apiConfiguration;
|
||||||
|
_notificationService = notificationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task HandleSendingErroredJobAlerts(List<BatchedErroredJobAlertRequest> batchedErroredJobAlertRequestList)
|
||||||
|
{
|
||||||
|
var erroredJobAlertRequests = CombineFromBatch(batchedErroredJobAlertRequestList);
|
||||||
|
|
||||||
|
return SendErroredJobsReport(erroredJobAlertRequests);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendErroredJobsReport(List<ErroredJobAlertRequest> erroredJobAlertRequests)
|
||||||
|
{
|
||||||
|
var groupedJobsByRecipients = GroupJobsByRecipients(erroredJobAlertRequests);
|
||||||
|
var result = true;
|
||||||
|
foreach (var recipientGroup in groupedJobsByRecipients)
|
||||||
|
{
|
||||||
|
var successful = await HandleBatchErroredJobs(recipientGroup.Value, recipientGroup.Key);
|
||||||
|
if (!successful)
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
throw new ServiceException($"Failure to Send Errored Jobs Report. Result: {result}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ErroredJobAlertRequest> CombineFromBatch(List<BatchedErroredJobAlertRequest> batchedErroredJobAlertRequestList)
|
||||||
|
{
|
||||||
|
var erroredJobAlertRequests = new List<ErroredJobAlertRequest>();
|
||||||
|
foreach (var batch in batchedErroredJobAlertRequestList)
|
||||||
|
{
|
||||||
|
erroredJobAlertRequests.AddRange(batch.ErroredJobAlertRequests);
|
||||||
|
}
|
||||||
|
return erroredJobAlertRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, List<ErroredJobAlertRequest>> GroupJobsByRecipients(List<ErroredJobAlertRequest> erroredJobAlertRequests)
|
||||||
|
{
|
||||||
|
var groupedRecipients = new Dictionary<string, List<ErroredJobAlertRequest>>();
|
||||||
|
foreach (var erroredJobAlertRequest in erroredJobAlertRequests)
|
||||||
|
{
|
||||||
|
AddRecipients(erroredJobAlertRequest, groupedRecipients);
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupedRecipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRecipients(ErroredJobAlertRequest erroredJbAlertRequest, Dictionary<string, List<ErroredJobAlertRequest>> groupedRecipients)
|
||||||
|
{
|
||||||
|
if (erroredJbAlertRequest.CurrentJob.JobErrorRecipients == null ||
|
||||||
|
erroredJbAlertRequest.CurrentJob.JobErrorRecipients.Count == 0)
|
||||||
|
{
|
||||||
|
AddRecipient(_apiConfiguration.DefaultErrorRecipient, erroredJbAlertRequest, groupedRecipients);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var jobErrorRecipient in erroredJbAlertRequest.CurrentJob.JobErrorRecipients)
|
||||||
|
{
|
||||||
|
AddRecipient(jobErrorRecipient.Recipient, erroredJbAlertRequest, groupedRecipients);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRecipient(string defaultErrorRecipient, ErroredJobAlertRequest erroredJobAlertRequest, Dictionary<string, List<ErroredJobAlertRequest>> groupedRecipients)
|
||||||
|
{
|
||||||
|
var recipientLower = defaultErrorRecipient.ToLower();
|
||||||
|
if (!groupedRecipients.ContainsKey(recipientLower))
|
||||||
|
{
|
||||||
|
groupedRecipients.Add(recipientLower, new List<ErroredJobAlertRequest>());
|
||||||
|
}
|
||||||
|
|
||||||
|
groupedRecipients[recipientLower].Add(erroredJobAlertRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HandleBatchErroredJobs(List<ErroredJobAlertRequest> erroredJobAlertRequests, string recipient)
|
||||||
|
{
|
||||||
|
string rowsOfErrors = "";
|
||||||
|
foreach (ErroredJobAlertRequest erroredJobAlertRequest in erroredJobAlertRequests)
|
||||||
|
{
|
||||||
|
var jobErrorDetail = SetUpJobErrorDetails(erroredJobAlertRequest);
|
||||||
|
if (jobErrorDetail != null)
|
||||||
|
{
|
||||||
|
rowsOfErrors += CreateErrorRow(jobErrorDetail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var emailTemplate = CreateBatchErroredJobsEmail(rowsOfErrors);
|
||||||
|
var successfullySent = await _notificationService.SendEmailAsync(recipient,
|
||||||
|
_apiConfiguration.BatchErroredJobsSubject,
|
||||||
|
emailTemplate);
|
||||||
|
return successfullySent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JobErrorDetail SetUpJobErrorDetails(ErroredJobAlertRequest erroredJobAlertRequest)
|
||||||
|
{
|
||||||
|
var jobErrorDetail = new JobErrorDetail()
|
||||||
|
{
|
||||||
|
JobName = erroredJobAlertRequest?.CurrentJob?.Name,
|
||||||
|
WorkItemName = erroredJobAlertRequest?.CurrentWorkItem?.Name,
|
||||||
|
JobStatus = erroredJobAlertRequest?.CurrentJob?.JobStatus.ToString(),
|
||||||
|
StartTime = erroredJobAlertRequest?.CurrentWorkItem?.CurrentWorkItemExecution?.StartedTimestamp.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
return jobErrorDetail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateErrorRow(JobErrorDetail jobErrorDetails)
|
||||||
|
{
|
||||||
|
using var errorRowReader = new StreamReader(_apiConfiguration.ErrorRowTemplate);
|
||||||
|
string errorRowOutput = errorRowReader.ReadToEnd();
|
||||||
|
|
||||||
|
foreach (var property in jobErrorDetails.GetType().GetProperties())
|
||||||
|
{
|
||||||
|
string value = property.GetValue(jobErrorDetails) == null ? string.Empty :
|
||||||
|
property.GetValue(jobErrorDetails).ToString();
|
||||||
|
errorRowOutput = errorRowOutput.Replace($"{{{property.Name}}}", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorRowOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateBatchErroredJobsEmail(string errorRows)
|
||||||
|
{
|
||||||
|
using var batchErroredJobsReader = new StreamReader(_apiConfiguration.BatchErroredJobsTemplate);
|
||||||
|
string batchErroredJobsOutput = batchErroredJobsReader.ReadToEnd();
|
||||||
|
return batchErroredJobsOutput.Replace("{TableRows}", errorRows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using LCU.Api.Internal.DispatcherLib.Repositories;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public class ExecutionMessageService : IExecutionMessageService
|
||||||
|
{
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
private readonly IExecutionMessageRepository _executionMessageRepository;
|
||||||
|
|
||||||
|
public ExecutionMessageService(IUnitOfWork unitOfWork,
|
||||||
|
IExecutionMessageRepository executionMessageRepository)
|
||||||
|
{
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
_executionMessageRepository = executionMessageRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveExecutionMessages(List<ExecutionMessage> executionMessageList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (executionMessageList == null || !executionMessageList.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var message in executionMessageList)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(message.Message))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var savedExecutionMessage = await SaveAsync(message);
|
||||||
|
ArgumentNullException.ThrowIfNull(savedExecutionMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "During SaveExecutionMessages there was an error. ");
|
||||||
|
throw new ServiceException($"During SaveExecutionMessages there was an error. " +
|
||||||
|
"ExceptionMessage: {exception.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveExecutionMessages(int workItemExecutionId, List<string> messageList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (messageList == null || !messageList.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var message in messageList)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(message))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var executionMessage = new ExecutionMessage(workItemExecutionId, message);
|
||||||
|
var savedExecutionMessage = await SaveAsync(executionMessage);
|
||||||
|
ArgumentNullException.ThrowIfNull(savedExecutionMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"During SaveExecutionMessages there was an error. " +
|
||||||
|
$"WorkItemExecutionId: {workItemExecutionId}");
|
||||||
|
throw new ServiceException($"During SaveExecutionMessages there was an error. " +
|
||||||
|
$"WorkItemExecutionId: {workItemExecutionId}, " +
|
||||||
|
$"ExceptionMessage: {exception.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ExecutionMessage> SaveAsync(ExecutionMessage workItemExecution)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _executionMessageRepository.AddAsync(workItemExecution);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure saving dispatcher Execution Messages", workItemExecution);
|
||||||
|
throw new ServiceException($"Failure saving dispatcher Execution Messages. ExceptionMessage: { exception.Message }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services
|
||||||
|
{
|
||||||
|
public interface ICleanUpJobSettingService
|
||||||
|
{
|
||||||
|
Task<IEnumerable<CleanUpJobSetting>> GetAsync();
|
||||||
|
Task<CleanUpJobSetting> GetByCleanUpJobSettingIdAsync(int cleanUpJobSettingId);
|
||||||
|
Task<CleanUpJobSetting> SaveAsync(CleanUpJobSetting request);
|
||||||
|
Task<CleanUpJobSetting> UpdateAsync(CleanUpJobSetting request);
|
||||||
|
Task DeleteAsync(int cleanUpJobSettingId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public interface ICleanUpJobTagService
|
||||||
|
{
|
||||||
|
Task<IEnumerable<CleanUpJobTag>> GetAsync();
|
||||||
|
Task<CleanUpJobTag> GetByCleanUpJobTagIdAsync(int cleanUpJobTagId);
|
||||||
|
Task<IEnumerable<CleanUpJobTag>> GetByCleanUpJobSettingIdAsync(int cleanUpJobSettingId);
|
||||||
|
Task<CleanUpJobTag> SaveAsync(CleanUpJobTag request);
|
||||||
|
Task DeleteAsync(int cleanUpJobTagId);
|
||||||
|
Task DeleteByCleanUpJobSettingIdAsync(int cleanUpJobSettingId);
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public interface IExecutionMessageService
|
||||||
|
{
|
||||||
|
Task SaveExecutionMessages(List<ExecutionMessage> executionMessageList);
|
||||||
|
Task SaveExecutionMessages(int workItemExecutionId, List<string> messageList);
|
||||||
|
Task<ExecutionMessage> SaveAsync(ExecutionMessage workItemExecution);
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Entities;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public interface IJobErrorRecipientService
|
||||||
|
{
|
||||||
|
void SaveList(ICollection<JobErrorRecipient> jobErrorRecipient);
|
||||||
|
void DeleteList(ICollection<JobErrorRecipient> jobErrorRecipient);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Results;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public interface IJobService
|
||||||
|
{
|
||||||
|
Task<PagedResult<Job>> GetFilteredJobs(Filter filter);
|
||||||
|
Task<IEnumerable<Job>> GetAsync();
|
||||||
|
Task<IEnumerable<Job>> GetInactiveAsync();
|
||||||
|
Task<IEnumerable<Job>> GetInactiveExcludeSoftDeletedTrackedAsync();
|
||||||
|
Task<IEnumerable<Job>> GetSoftDeletedTrackedAsync(DateTime cutOffDate);
|
||||||
|
Task<Job> GetByIdAsync(int id);
|
||||||
|
Task<Job> GetByIdExcludingWorkItemsAsync(int id);
|
||||||
|
Task<Job> GetByWorkItemIdAsync(int workItemId);
|
||||||
|
Task<Job> GetByWorkItemIdExcludingWorkItemsAsync(int workItemId);
|
||||||
|
Task<Job> GetByWorkItemIdExcludingWorkItemsIncludeErrorRecipientsAsync(int workItemId);
|
||||||
|
Task<IEnumerable<Job>> GetJobsReadyToBeScheduled();
|
||||||
|
Task<IEnumerable<Job>> GetErrorMonitoringJobs();
|
||||||
|
Task<Job> SaveAsync(Job job);
|
||||||
|
Task CompleteJobAndWorkItem(Job job, WorkItemExecution currentWorkItemExecution, StatusType finalStatus);
|
||||||
|
Task CompleteWorkItemAndContinueProcessingJob(Job job, WorkItemExecution currentWorkItemExecution, StatusType finalStatus);
|
||||||
|
Task<Job> UpdateAsync(Job job);
|
||||||
|
Task<Job> UpdateExcludingWorkItemsAsyncWithoutSave(Job job);
|
||||||
|
Task<Job> RemoveSoftDeleteById(int id);
|
||||||
|
Task<Job> RunJobNowAsync(Job job);
|
||||||
|
Task<Job> RunJobNowByIdAsync(int id);
|
||||||
|
Task<Job> DeactivateJobAsync(Job job);
|
||||||
|
Task<Job> RescheduleJobAsync(Job job);
|
||||||
|
Task DeleteAsync(int jobId);
|
||||||
|
Task DeleteFromCleanupAsync(Job job);
|
||||||
|
Task<Job> CopyJobAsync(CopyJobRequest request);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public interface IJobTagService
|
||||||
|
{
|
||||||
|
Task<IEnumerable<JobTag>> GetAsync();
|
||||||
|
Task<IEnumerable<JobTag>> GetByTagIdAsync(int tagId);
|
||||||
|
Task<JobTag> SaveAsync(JobTag jobTag);
|
||||||
|
Task Delete(int jobTagId);
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public interface INotificationService
|
||||||
|
{
|
||||||
|
Task<bool> SendEmailAsync(string recipient, string subject, string emailTemplate);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public interface ITagService
|
||||||
|
{
|
||||||
|
Task<IEnumerable<Tag>> GetAsync();
|
||||||
|
Task<Tag> GetByIdAsync(int tagId);
|
||||||
|
Task<Tag> SaveAsync(Tag tag);
|
||||||
|
Task<Tag> UpdateAsync(Tag tag);
|
||||||
|
Task DeleteAsync(int tagId);
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public interface IWorkItemExecutionService
|
||||||
|
{
|
||||||
|
Task<WorkItemExecution> GetByIdAsync(int workItemExecutionId);
|
||||||
|
Task<WorkItemExecution> UpdateCompleteWorkItemExecution(WorkItemExecution workItemExecution, StatusType workItemFinalStatus);
|
||||||
|
Task<WorkItemExecution> UpdateCompleteWorkItemExecutionWithoutSave(WorkItemExecution workItemExecution, StatusType workItemFinalStatus);
|
||||||
|
Task<WorkItemExecution> UpdateAsync(WorkItemExecution workItemExecution);
|
||||||
|
Task<WorkItemExecution> CreateWorkItemExecution(int workItemId);
|
||||||
|
Task<WorkItemExecution> SaveAsync(WorkItemExecution workItemExecution);
|
||||||
|
Task<WorkItemExecution> UpdateProcessingWorkItemExecution(WorkItemExecution workItemExecution);
|
||||||
|
Task<WorkItemExecution> UpdateFailedWorkItemExecution(WorkItemExecution workItemExecution);
|
||||||
|
Task<IEnumerable<WorkItemExecution>> GetAllUnFinishedWorkItemExecutions(int workItemExecutionId);
|
||||||
|
Task<IEnumerable<WorkItemExecution>> GetAllByWorkItemId(int workItemId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public interface IWorkItemService
|
||||||
|
{
|
||||||
|
Task<WorkItem> GetByIdAsync(int workItemId);
|
||||||
|
Task<IEnumerable<WorkItem>> GetByJobIdAsync(int jobId);
|
||||||
|
Task<IEnumerable<WorkItem>> GetByJobIdWithExecutionsAsync(int jobId);
|
||||||
|
Task<WorkItem> GetFirstOrDefaultWorkItemByJobId(int jobId);
|
||||||
|
Task<WorkItem> GetNextWorkItemByJobId(int jobId, int currentWorkItemOrder);
|
||||||
|
void SaveList(ICollection<WorkItem> workItems);
|
||||||
|
Task<ICollection<WorkItem>> PopulateActionFieldsAndUpdateList(ICollection<WorkItem> workItems);
|
||||||
|
Task<WorkItem> ResetCanceledJobCounter(WorkItem workItem);
|
||||||
|
Task<WorkItem> IncreaseCanceledJobCounter(WorkItem workItem);
|
||||||
|
Task<WorkItem> UpdateAsync(WorkItem workItem);
|
||||||
|
Task DeleteList(ICollection<WorkItem> workItems);
|
||||||
|
WorkItem PopulateActionFields(WorkItem workItem);
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using LCU.Api.Internal.DispatcherLib.Repositories;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public class JobErrorRecipientService : IJobErrorRecipientService
|
||||||
|
{
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
private readonly IJobErrorRecipientRepository _jobErrorRecipientRepository;
|
||||||
|
|
||||||
|
public JobErrorRecipientService(IUnitOfWork unitOfWork,
|
||||||
|
IJobErrorRecipientRepository jobErrorRecipientRepository)
|
||||||
|
{
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
_jobErrorRecipientRepository = jobErrorRecipientRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveList(ICollection<JobErrorRecipient> jobErrorRecipient)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_jobErrorRecipientRepository.AddList(jobErrorRecipient);
|
||||||
|
_unitOfWork.Complete();
|
||||||
|
}
|
||||||
|
catch (SqlException ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, $"SQL Failure saving list of job error recipients.");
|
||||||
|
throw new ServiceException($"SQL Failure saving list of job error recipients.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, $"Failure saving list of job error recipients.");
|
||||||
|
throw new ServiceException($"Failure saving list of job error recipients.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteList(ICollection<JobErrorRecipient> jobErrorRecipient)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_jobErrorRecipientRepository.RemoveList(jobErrorRecipient);
|
||||||
|
_unitOfWork.Complete();
|
||||||
|
}
|
||||||
|
catch (SqlException ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, $"SQL Failure removing list of job error recipients.");
|
||||||
|
throw new ServiceException($"SQL Failure removing list of job error recipients.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, $"Failure removing list of job error recipients.");
|
||||||
|
throw new ServiceException($"Failure removing list of job error recipients.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,583 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using LCU.Api.Internal.DispatcherLib.Repositories;
|
||||||
|
using LCU.Api.Internal.DispatcherSharedLib.Utilities;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
using LCU.Library.Internal.TypeExtensions.ObjectExtensions;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Results;
|
||||||
|
using LCU.Library.Internal.Logging.Performance;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public class JobService : IJobService
|
||||||
|
{
|
||||||
|
private readonly IJobRepository _jobRepository;
|
||||||
|
private readonly IWorkItemService _workItemService;
|
||||||
|
private readonly IWorkItemExecutionService _workItemExecutionService;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
private readonly ApiConfiguration _apiConfiguration;
|
||||||
|
|
||||||
|
public JobService(IUnitOfWork unitOfWork,
|
||||||
|
IJobRepository jobRepository,
|
||||||
|
IWorkItemService workItemService,
|
||||||
|
IWorkItemExecutionService workItemExecutionService,
|
||||||
|
ApiConfiguration apiConfiguration)
|
||||||
|
{
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
_jobRepository = jobRepository;
|
||||||
|
_workItemService = workItemService;
|
||||||
|
_workItemExecutionService = workItemExecutionService;
|
||||||
|
_apiConfiguration = apiConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PagedResult<Job>> GetFilteredJobs(Filter filter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var performanceMonitorId = Guid.NewGuid();
|
||||||
|
var getFilteredJobsQueryMonitor = new PerformanceMonitor("GetFilteredJobs", performanceMonitorId);
|
||||||
|
|
||||||
|
var filteredJobs = await _jobRepository.FilteredListAsync(filter,
|
||||||
|
_apiConfiguration.NumberOfWorkItemExecutions,
|
||||||
|
_apiConfiguration.NumberOfExecutionMessages);
|
||||||
|
|
||||||
|
getFilteredJobsQueryMonitor.LogPerformance();
|
||||||
|
|
||||||
|
return filteredJobs;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving filtered dispatcher jobs.");
|
||||||
|
throw new ServiceException("Failure retrieving filtered dispatcher jobs.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Job>> GetAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _jobRepository.ListAsync(_apiConfiguration.NumberOfWorkItemExecutions, _apiConfiguration.NumberOfExecutionMessages);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving dispatcher jobs.");
|
||||||
|
throw new ServiceException("Failure retrieving dispatcher jobs.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Job>> GetInactiveAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _jobRepository.ListInactiveAsync(_apiConfiguration.NumberOfWorkItemExecutions, _apiConfiguration.NumberOfExecutionMessages);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving inactive dispatcher jobs.");
|
||||||
|
throw new ServiceException("Failure retrieving inactive dispatcher jobs.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Job>> GetInactiveExcludeSoftDeletedTrackedAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _jobRepository.ListInactiveExcludeSoftDeletedTrackedAsync(_apiConfiguration.NumberOfWorkItemExecutions, _apiConfiguration.NumberOfExecutionMessages);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving non-SoftDeleted inactive dispatcher jobs.");
|
||||||
|
throw new ServiceException("Failure retrieving non-SoftDeleted inactive dispatcher jobs.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Job>> GetSoftDeletedTrackedAsync(DateTime cutOffDate)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _jobRepository.ListSoftDeletedTrackedAsync(_apiConfiguration.NumberOfWorkItemExecutions, _apiConfiguration.NumberOfExecutionMessages, cutOffDate);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving soft deleted dispatcher jobs.");
|
||||||
|
throw new ServiceException("Failure retrieving soft deleted dispatcher jobs.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> GetByIdAsync(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var job = await _jobRepository.FindByIdAsync(id,
|
||||||
|
_apiConfiguration.NumberOfWorkItemExecutions,
|
||||||
|
_apiConfiguration.NumberOfExecutionMessages);
|
||||||
|
ArgumentNullException.ThrowIfNull(job);
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher jobs with id {id}.");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher jobs with id {id}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> GetByIdExcludingWorkItemsAsync(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var job = await _jobRepository.FindByIdExcludingWorkItemsAsync(id);
|
||||||
|
ArgumentNullException.ThrowIfNull(job);
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher jobs with id {id}.");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher jobs with id {id}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> GetByWorkItemIdAsync(int workItemId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var job = await _jobRepository.FindByWorkItemIdAsync(workItemId,
|
||||||
|
_apiConfiguration.NumberOfWorkItemExecutions,
|
||||||
|
_apiConfiguration.NumberOfExecutionMessages);
|
||||||
|
ArgumentNullException.ThrowIfNull(job);
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher jobs by WorkItemId {workItemId}.");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher jobs by WorkItemId {workItemId}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> GetByWorkItemIdExcludingWorkItemsAsync(int workItemId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var job = await _jobRepository.FindByWorkItemIdExcludingWorkItemsAsync(workItemId);
|
||||||
|
ArgumentNullException.ThrowIfNull(job);
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher jobs excluding WorkItems by WorkItemId {workItemId}.");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher jobs excluding WorkItems by WorkItemId {workItemId}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> GetByWorkItemIdExcludingWorkItemsIncludeErrorRecipientsAsync(int workItemId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var job = await _jobRepository.FindByWorkItemIdExcludingWorkItemsIncludeErrorRecipientsAsync(workItemId);
|
||||||
|
ArgumentNullException.ThrowIfNull(job);
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher jobs excluding WorkItems by WorkItemId {workItemId}.");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher jobs excluding WorkItems by WorkItemId {workItemId}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Job>> GetJobsReadyToBeScheduled()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobRepository.FindJobsReadyToBeScheduled(DateTime.Now);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure getting pending Jobs");
|
||||||
|
throw new ServiceException("Failure getting pending Jobs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Job>> GetErrorMonitoringJobs()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobRepository.FindJobsPastDateByStatuses(DateTime.Now,
|
||||||
|
_apiConfiguration.NumberOfWorkItemExecutions,
|
||||||
|
_apiConfiguration.NumberOfExecutionMessages,
|
||||||
|
_apiConfiguration.JobStatusesToMonitorByTimeLimit);
|
||||||
|
ArgumentNullException.ThrowIfNull(result);
|
||||||
|
var erroredAndStuckJobs = result.Where(job => job.JobStatus == JobStatus.Failed).ToList();
|
||||||
|
if (_apiConfiguration.JobStatusesToMonitorByTimeLimit is not null && _apiConfiguration.JobStatusesToMonitorByTimeLimit.Count() > 0)
|
||||||
|
{
|
||||||
|
erroredAndStuckJobs.AddRange(GetJobsInStatusLongerThanTimeLimit(result.Except(erroredAndStuckJobs).ToList(),
|
||||||
|
_apiConfiguration.ProcessingLimitInMinutes));
|
||||||
|
}
|
||||||
|
|
||||||
|
return erroredAndStuckJobs;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure getting next errored and stuck in processing Jobs");
|
||||||
|
throw new ServiceException("Failure getting next errored and stuck in processing Jobs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Job> GetJobsInStatusLongerThanTimeLimit(List<Job> matchingStatusJobs, int processingLimitInMinutes)
|
||||||
|
{
|
||||||
|
var stuckInStatusJobs = new List<Job>();
|
||||||
|
|
||||||
|
foreach (var job in matchingStatusJobs)
|
||||||
|
{
|
||||||
|
var currentWorkItem = job.WorkItems.FirstOrDefault(wi => wi.CurrentWorkItemExecution != null);
|
||||||
|
if (currentWorkItem == null)
|
||||||
|
{
|
||||||
|
Log.Warning($"Current Work Item came back as null when attempting to get stuck in processing Jobs. JobId: {job.JobId}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var startTime = Convert.ToDateTime(currentWorkItem.CurrentWorkItemExecution.StartedTimestamp);
|
||||||
|
var minutesInStatus = DateTime.Now.Subtract(startTime).TotalMinutes;
|
||||||
|
if (minutesInStatus > processingLimitInMinutes)
|
||||||
|
{
|
||||||
|
stuckInStatusJobs.Add(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stuckInStatusJobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> SaveAsync(Job job)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobRepository.AddAsync(job);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
ArgumentNullException.ThrowIfNull(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure saving dispatcher job", job);
|
||||||
|
throw new ServiceException("Failure saving dispatcher job");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CompleteJobAndWorkItem(Job job, WorkItemExecution currentWorkItemExecution, StatusType workItemFinalStatus)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
job.ResetRunTimeAndStatus();
|
||||||
|
CronHelpers.SetNextScheduledRunTime(ref job);
|
||||||
|
var updatedJob = await UpdateExcludingWorkItemsAsyncWithoutSave(job);
|
||||||
|
if (updatedJob == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failure to setup update for job. Job:{JsonConvert.SerializeObject(job)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedWorkItemExecution = await _workItemExecutionService.UpdateCompleteWorkItemExecutionWithoutSave(currentWorkItemExecution, workItemFinalStatus);
|
||||||
|
if (updatedWorkItemExecution == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failure to setup update for WorkItemExecution. WorkItemExecution:{JsonConvert.SerializeObject(currentWorkItemExecution)}");
|
||||||
|
}
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
Log.Information("Job Id {JobId} was saved with status {Status}", job.JobId, job.JobStatus);
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
var message = $"SQL Failure to Complete Job and WorkItemExecution. JobId: {job.JobId}, WorkItemExecutionId: {currentWorkItemExecution.WorkItemExecutionId}.";
|
||||||
|
Log.Error(sqlException, message);
|
||||||
|
throw new ServiceException($"{message} ExceptionMessage: {sqlException.Message}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
var message = $"Failure to Complete Job and WorkItemExecution. JobId: {job.JobId}, WorkItemExecutionId: {currentWorkItemExecution.WorkItemExecutionId}.";
|
||||||
|
Log.Error(exception, message);
|
||||||
|
throw new ServiceException($"{message} ExceptionMessage: {exception.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CompleteWorkItemAndContinueProcessingJob(Job job, WorkItemExecution currentWorkItemExecution, StatusType workItemFinalStatus)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
job.JobStatus = JobStatus.Processing;
|
||||||
|
var updatedJob = await UpdateExcludingWorkItemsAsyncWithoutSave(job);
|
||||||
|
if (updatedJob == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failure to setup update for job. Job:{JsonConvert.SerializeObject(job)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedWorkItemExecution = await _workItemExecutionService.UpdateCompleteWorkItemExecutionWithoutSave(currentWorkItemExecution, workItemFinalStatus);
|
||||||
|
if (updatedWorkItemExecution == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failure to setup update for WorkItemExecution. WorkItemExecution:{JsonConvert.SerializeObject(currentWorkItemExecution)}");
|
||||||
|
}
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
Log.Information("Job Id {JobId} was saved with status {Status}", job.JobId, job.JobStatus);
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
var message = $"SQL Failure to Complete Job and WorkItemExecution. JobId: {job.JobId}, WorkItemExecutionId: {currentWorkItemExecution.WorkItemExecutionId}.";
|
||||||
|
Log.Error(sqlException, message);
|
||||||
|
throw new ServiceException($"{message} ExceptionMessage: {sqlException.Message}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
var message = $"Failure to Complete Job and WorkItemExecution. JobId: {job.JobId}, WorkItemExecutionId: {currentWorkItemExecution.WorkItemExecutionId}.";
|
||||||
|
Log.Error(exception, message);
|
||||||
|
throw new ServiceException($"{message} ExceptionMessage: {exception.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> UpdateAsync(Job job)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var updatedJob = await _jobRepository.Update(job);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
|
||||||
|
return updatedJob;
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, "SQL Failure updating dispatcher job", job);
|
||||||
|
throw new ServiceException("SQL Failure updating dispatcher job");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure updating dispatcher job", job);
|
||||||
|
throw new ServiceException("Failure updating dispatcher job");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> UpdateExcludingWorkItemsAsyncWithoutSave(Job job)
|
||||||
|
{
|
||||||
|
return await _jobRepository.Update(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> RemoveSoftDeleteById(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var existing = await GetByIdAsync(id);
|
||||||
|
ArgumentNullException.ThrowIfNull(existing);
|
||||||
|
|
||||||
|
existing.SoftDeleteDate = null;
|
||||||
|
_jobRepository.Update(existing);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure to remove soft delete date for dispatcher job. JobId: {id}");
|
||||||
|
throw new ServiceException($"Failure to remove soft delete date for dispatcher job. JobId: {id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(int jobId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var job = await _jobRepository.FindByIdAsync(jobId,
|
||||||
|
_apiConfiguration.NumberOfWorkItemExecutions,
|
||||||
|
_apiConfiguration.NumberOfExecutionMessages);
|
||||||
|
ArgumentNullException.ThrowIfNull(job);
|
||||||
|
|
||||||
|
_jobRepository.Remove(job);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, $"SQL Failure removing dispatcher job. JobId: {jobId}");
|
||||||
|
throw new ServiceException($"SQL Failure removing dispatcher job. JobId: {jobId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure removing dispatcher job. JobId: {jobId}");
|
||||||
|
throw new ServiceException($"Failure removing dispatcher job. JobId: {jobId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteFromCleanupAsync(Job job)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(job);
|
||||||
|
|
||||||
|
_jobRepository.Remove(job);
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, $"SQL Failure removing dispatcher job. JobId: {job.JobId}");
|
||||||
|
throw new ServiceException($"SQL Failure removing dispatcher job. JobId: {job.JobId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure removing dispatcher job. JobId: {job.JobId}");
|
||||||
|
throw new ServiceException($"Failure removing dispatcher job. JobId: {job.JobId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> RunJobNowAsync(Job job)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
job = await ResetJob(job);
|
||||||
|
job.NextRunTime = DateTime.Now;
|
||||||
|
job.JobStatus = JobStatus.ReadyToSchedule;
|
||||||
|
var updatedJob = await UpdateAsync(job);
|
||||||
|
return updatedJob;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure to set job to run now.");
|
||||||
|
throw new ServiceException("Failure to set job to run now.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> RunJobNowByIdAsync(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var job = await GetByIdAsync(id);
|
||||||
|
var updatedJob = await RunJobNowAsync(job);
|
||||||
|
return updatedJob;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure to set job to run now by id. Id: {id}");
|
||||||
|
throw new ServiceException($"Failure to set job to run now by id. Id: {id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> DeactivateJobAsync(Job job)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
job = await ResetJob(job);
|
||||||
|
var updatedJob = await UpdateAsync(job);
|
||||||
|
return updatedJob;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
Log.Error(exception, "Failure to deactivate job.");
|
||||||
|
throw new ServiceException("Failure to deactivate job.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> RescheduleJobAsync(Job job)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
job = await ResetJob(job);
|
||||||
|
CronHelpers.SetNextScheduledRunTime(ref job);
|
||||||
|
var updatedJob = await UpdateAsync(job);
|
||||||
|
Log.Information("Job Id {JobId} was saved with status {Status}", job.JobId, job.JobStatus);
|
||||||
|
return updatedJob;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure to reschedule job.");
|
||||||
|
throw new ServiceException("Failure to reschedule job.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> ResetJob(Job job)
|
||||||
|
{
|
||||||
|
foreach (var workItem in job.WorkItems)
|
||||||
|
{
|
||||||
|
var executionsToReset = await _workItemExecutionService.GetAllUnFinishedWorkItemExecutions(workItem.WorkItemId);
|
||||||
|
foreach (var workItemExecution in executionsToReset)
|
||||||
|
{
|
||||||
|
workItemExecution.StartedTimestamp = workItemExecution.StartedTimestamp == null ?
|
||||||
|
DateTime.Now :
|
||||||
|
workItemExecution.StartedTimestamp;
|
||||||
|
workItemExecution.CompletedTimestamp = workItemExecution.CompletedTimestamp == null ?
|
||||||
|
DateTime.Now :
|
||||||
|
workItemExecution.CompletedTimestamp;
|
||||||
|
|
||||||
|
var updatedExecution = await _workItemExecutionService.UpdateAsync(workItemExecution);
|
||||||
|
ArgumentNullException.ThrowIfNull(updatedExecution);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
job.ResetRunTimeAndStatus();
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Job> CopyJobAsync(CopyJobRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var job = (await GetByIdAsync(request.JobId)).DeepCopy();
|
||||||
|
|
||||||
|
if (job == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"Job with id {request.JobId} not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
job.Name = $"(COPY) {job.Name}";
|
||||||
|
job.JobId = 0;
|
||||||
|
job.CronSchedule = null;
|
||||||
|
job.CreatedAt = DateTime.Now;
|
||||||
|
job.ModifiedDate = null;
|
||||||
|
job.ResetRunTimeAndStatus();
|
||||||
|
|
||||||
|
foreach (var errorRecipient in job.JobErrorRecipients)
|
||||||
|
{
|
||||||
|
errorRecipient.JobErrorRecipientId = 0;
|
||||||
|
errorRecipient.JobId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.ChangeEnvironment)
|
||||||
|
{
|
||||||
|
job.JobTags = null;
|
||||||
|
|
||||||
|
foreach (var workItem in job.WorkItems)
|
||||||
|
{
|
||||||
|
workItem.WorkItemId = 0;
|
||||||
|
workItem.JobId = 0;
|
||||||
|
workItem.Url = null;
|
||||||
|
workItem.WorkItemExecutions = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var workItem in job.WorkItems)
|
||||||
|
{
|
||||||
|
workItem.WorkItemId = 0;
|
||||||
|
workItem.JobId = 0;
|
||||||
|
workItem.Url = null;
|
||||||
|
workItem.WorkItemExecutions = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var jobTag in job.JobTags)
|
||||||
|
{
|
||||||
|
jobTag.JobTagId = 0;
|
||||||
|
jobTag.JobId = 0;
|
||||||
|
jobTag.Tag = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var savedJob = await SaveAsync(job);
|
||||||
|
|
||||||
|
if (savedJob == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Failure to save new job.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return savedJob;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
var message = $"Failure to copy dispatcher job. Request: {JsonConvert.SerializeObject(request)}";
|
||||||
|
Log.Error(exception, message);
|
||||||
|
throw new ServiceException($"{message}. Exception: {exception.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using LCU.Api.Internal.DispatcherLib.Repositories;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public class JobTagService : IJobTagService
|
||||||
|
{
|
||||||
|
private readonly IJobTagRepository _jobTagRepository;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
|
||||||
|
public JobTagService(IUnitOfWork unitOfWork,
|
||||||
|
IJobTagRepository jobTagRepository)
|
||||||
|
{
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
_jobTagRepository = jobTagRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<JobTag>> GetAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _jobTagRepository.ListAsync();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving JobTags.");
|
||||||
|
throw new ServiceException("Failure retrieving JobTags.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<JobTag>> GetByTagIdAsync(int tagId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(tagId);
|
||||||
|
return await _jobTagRepository.FindByTagIdAsync(tagId);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher Job Tags. TagId: {tagId}");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher Job Tags. TagId: {tagId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<JobTag> SaveAsync(JobTag jobTag)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _jobTagRepository.AddAsync(jobTag);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
ArgumentNullException.ThrowIfNull(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure saving dispatcher JobTag. JobTagId: {jobTag.JobTagId}");
|
||||||
|
throw new ServiceException($"Failure saving dispatcher JobTag. JobTagId: {jobTag.JobTagId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Delete(int jobTagId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jobTag = await _jobTagRepository.FindByIdAsync(jobTagId);
|
||||||
|
ArgumentNullException.ThrowIfNull(jobTag);
|
||||||
|
_jobTagRepository.Remove(jobTag);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, $"SQL Failure removing dispatcher JobTag. JobTagId: {jobTagId}");
|
||||||
|
throw new ServiceException($"SQL Failure removing dispatcher JobTag. JobTagId: {jobTagId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure removing dispatcher JobTag. JobTagId: {jobTagId}");
|
||||||
|
throw new ServiceException($"Failure removing dispatcher JobTag. JobTagId: {jobTagId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using LCU.Library.Internal.NotificationsModels.Models;
|
||||||
|
using LCU.Api.Internal.DispatcherSharedLib.Configurations;
|
||||||
|
using LCU.Api.Internal.DispatcherSharedLib.Utilities;
|
||||||
|
using LCU.Api.Internal.Dispatcher.DataAccess;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public class NotificationService : INotificationService
|
||||||
|
{
|
||||||
|
private readonly INotificationsApiDao _notificationsApiDao;
|
||||||
|
|
||||||
|
public NotificationService(INotificationsApiDao notificationsApiDao)
|
||||||
|
{
|
||||||
|
_notificationsApiDao = notificationsApiDao;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SendEmailAsync(string recipient, string subject, string emailTemplate)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var emailRequest = new EmailRequest
|
||||||
|
{
|
||||||
|
Recipient = new List<string>() { recipient },
|
||||||
|
Subject = subject,
|
||||||
|
Body = emailTemplate
|
||||||
|
};
|
||||||
|
|
||||||
|
await _notificationsApiDao.SendEmailAsync(emailRequest);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure to send email from Dispatcher");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
|
||||||
|
public interface IQueueCompletionService
|
||||||
|
{
|
||||||
|
Task QueueCompletion(DispatcherReturnResponse request, List<string> executionMessages = null, bool successful = true);
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.QueueServices
|
||||||
|
{
|
||||||
|
public interface IQueueDetermineOperationService
|
||||||
|
{
|
||||||
|
Task QueueDetermineOperation(Job job, WorkItem workItem, DetermineOperationType type);
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
|
||||||
|
public interface IQueueExecutionService
|
||||||
|
{
|
||||||
|
Task QueueExecution(WorkItemExecution request);
|
||||||
|
Task QueueRetryExecution(WorkItemExecution request);
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.QueueServices
|
||||||
|
{
|
||||||
|
public interface IQueueJobFailureService
|
||||||
|
{
|
||||||
|
public Task QueueJobFailure(int jobId, int workItemId, List<string> messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.QueueServices
|
||||||
|
{
|
||||||
|
public interface IQueueSendErroredJobAlertService
|
||||||
|
{
|
||||||
|
public Task QueueSendErroredJobAlert(BatchedErroredJobAlertRequest request);
|
||||||
|
public Task QueueSendErroredJobAlert(Job job, WorkItem workItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using MassTransit;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using ApiResponse = LCU.Library.Internal.ApiResponse.Models;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
|
||||||
|
public class QueueCompletionService : IQueueCompletionService
|
||||||
|
{
|
||||||
|
private readonly ISendEndpoint _sendEndpoint;
|
||||||
|
private readonly ReceiveEndpointConfigurations _queueReceiveEndpointsConfiguration;
|
||||||
|
|
||||||
|
public QueueCompletionService(IBusControl busControl,
|
||||||
|
ReceiveEndpointConfigurations queueReceiveEndpointsConfiguration)
|
||||||
|
{
|
||||||
|
_queueReceiveEndpointsConfiguration = queueReceiveEndpointsConfiguration;
|
||||||
|
_sendEndpoint = busControl.GetSendEndpoint(_queueReceiveEndpointsConfiguration.CompletionReceiveEndpoint.ReceiveEndpointUri).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task QueueCompletion(DispatcherReturnResponse request, List<string> messages = null, bool successful = true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(request);
|
||||||
|
|
||||||
|
var response = new ApiResponse.Response()
|
||||||
|
{
|
||||||
|
Successful = successful,
|
||||||
|
ResponseStack = messages == null ? new List<string>() : messages,
|
||||||
|
ResponsePayload = JsonConvert.SerializeObject(request)
|
||||||
|
};
|
||||||
|
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||||
|
await _sendEndpoint.Send(response, cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Fatal(exception, $"Failed to queue completion workitem request. WorkItemId: {request?.WorkItemId}");
|
||||||
|
throw new ServiceException($"Failed to queue completion workitem request. WorkItemId: {request?.WorkItemId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using MassTransit;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using ApiResponse = LCU.Library.Internal.ApiResponse.Models;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.QueueServices
|
||||||
|
{
|
||||||
|
public class QueueDetermineOperationService : IQueueDetermineOperationService
|
||||||
|
{
|
||||||
|
private readonly ISendEndpoint _sendEndpoint;
|
||||||
|
private readonly ReceiveEndpointConfigurations _queueReceiveEndpointsConfiguration;
|
||||||
|
|
||||||
|
public QueueDetermineOperationService(IBusControl busControl,
|
||||||
|
ReceiveEndpointConfigurations queueReceiveEndpointsConfiguration)
|
||||||
|
{
|
||||||
|
_queueReceiveEndpointsConfiguration = queueReceiveEndpointsConfiguration;
|
||||||
|
_sendEndpoint = busControl.GetSendEndpoint(_queueReceiveEndpointsConfiguration.DetermineOperationReceiveEndpoint.ReceiveEndpointUri).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task QueueDetermineOperation(Job job, WorkItem workItem, DetermineOperationType type)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new DetermineOperationRequest
|
||||||
|
{
|
||||||
|
CurrentJob = job,
|
||||||
|
CurrentWorkItem = workItem,
|
||||||
|
DetermineOperationType = type
|
||||||
|
};
|
||||||
|
|
||||||
|
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 determine operation request. JobName: {job.Name} WorkItemId: {workItem.WorkItemId}");
|
||||||
|
throw new ServiceException($"Failed to queue determine operation request. JobName: {job.Name} WorkItemId: {workItem.WorkItemId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using MassTransit;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.QueueServices;
|
||||||
|
|
||||||
|
public class QueueExecutionService : IQueueExecutionService
|
||||||
|
{
|
||||||
|
private readonly ISendEndpoint _sendEndpoint;
|
||||||
|
private readonly IWorkItemExecutionService _workItemExecutionService;
|
||||||
|
private readonly ReceiveEndpointConfigurations _queueReceiveEndpointsConfiguration;
|
||||||
|
|
||||||
|
public QueueExecutionService(IBusControl busControl,
|
||||||
|
IWorkItemExecutionService workItemExecutionService,
|
||||||
|
ReceiveEndpointConfigurations queueReceiveEndpointsConfiguration)
|
||||||
|
{
|
||||||
|
_queueReceiveEndpointsConfiguration = queueReceiveEndpointsConfiguration;
|
||||||
|
_workItemExecutionService = workItemExecutionService;
|
||||||
|
_sendEndpoint = busControl.GetSendEndpoint(_queueReceiveEndpointsConfiguration.ExecutionReceiveEndpoint.ReceiveEndpointUri).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task QueueExecution(WorkItemExecution 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 workitemexecution request. WorkItemExecutionId: {request?.WorkItemExecutionId}");
|
||||||
|
throw new ServiceException($"Failed to queue workitemexecution request. WorkItemExecutionId: {request?.WorkItemExecutionId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task QueueRetryExecution(WorkItemExecution request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var updatedWorkItemExection = _workItemExecutionService.UpdateProcessingWorkItemExecution(request);
|
||||||
|
|
||||||
|
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 workitemexecution request. WorkItemExecutionId: {request?.WorkItemExecutionId}");
|
||||||
|
throw new ServiceException($"Failed to queue workitemexecution request. WorkItemExecutionId: {request?.WorkItemExecutionId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Library.Internal.ApiResponse.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Entities;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using MassTransit;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using ApiResponse = LCU.Library.Internal.ApiResponse.Models;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.QueueServices
|
||||||
|
{
|
||||||
|
public class QueueJobFailureService : IQueueJobFailureService
|
||||||
|
{
|
||||||
|
private readonly ISendEndpoint _sendEndpoint;
|
||||||
|
private readonly ReceiveEndpointConfigurations _queueReceiveEndpointsConfiguration;
|
||||||
|
|
||||||
|
public QueueJobFailureService(IBusControl busControl,
|
||||||
|
ReceiveEndpointConfigurations queueReceiveEndpointsConfiguration)
|
||||||
|
{
|
||||||
|
_queueReceiveEndpointsConfiguration = queueReceiveEndpointsConfiguration;
|
||||||
|
_sendEndpoint = busControl.GetSendEndpoint(_queueReceiveEndpointsConfiguration.JobFailureReceiveEndpoint.ReceiveEndpointUri).Result;
|
||||||
|
}
|
||||||
|
public async Task QueueJobFailure(int jobId, int workItemId, List<string> messages)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new JobFailureRequest()
|
||||||
|
{
|
||||||
|
JobId = jobId,
|
||||||
|
WorkItemId = workItemId,
|
||||||
|
ExecutionMessages = messages
|
||||||
|
};
|
||||||
|
|
||||||
|
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 job failure request. JobId: {jobId}, WorkItemId: {workItemId}");
|
||||||
|
throw new ServiceException($"Failed to queue job failure request. JobId: {jobId}, WorkItemId: {workItemId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
using LCU.Api.Internal.MaidBotLib.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models.Requests;
|
||||||
|
using MassTransit;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using ApiResponse = LCU.Library.Internal.ApiResponse.Models;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services.QueueServices
|
||||||
|
{
|
||||||
|
public class QueueSendErroredJobAlertService : IQueueSendErroredJobAlertService
|
||||||
|
{
|
||||||
|
private readonly ISendEndpoint _sendEndpoint;
|
||||||
|
private readonly ReceiveEndpointConfigurations _queueReceiveEndpointsConfiguration;
|
||||||
|
|
||||||
|
public QueueSendErroredJobAlertService(IBusControl busControl,
|
||||||
|
ReceiveEndpointConfigurations queueReceiveEndpointConfigurations)
|
||||||
|
{
|
||||||
|
_queueReceiveEndpointsConfiguration = queueReceiveEndpointConfigurations;
|
||||||
|
_sendEndpoint = busControl.GetSendEndpoint(_queueReceiveEndpointsConfiguration.SendErroredJobAlertReceiveEndpoint.ReceiveEndpointUri).Result;
|
||||||
|
}
|
||||||
|
public async Task QueueSendErroredJobAlert(BatchedErroredJobAlertRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(request);
|
||||||
|
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||||
|
await _sendEndpoint.Send(request, cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
var workItemIds = string.Join(",",
|
||||||
|
request.ErroredJobAlertRequests
|
||||||
|
.Where(x => x.CurrentWorkItem != null)
|
||||||
|
.Select(x => x.CurrentWorkItem.WorkItemId));
|
||||||
|
|
||||||
|
Log.ForContext(Enum.GetName(typeof(PropertyEnum), PropertyEnum.Group),
|
||||||
|
Enum.GetName(typeof(GroupEnum), GroupEnum.ITDevelopment))
|
||||||
|
.Fatal(exception, $"Failed to queue send errored job alert workitem requests. WorkItemIds: {workItemIds}");
|
||||||
|
throw new ServiceException($"Failed to queue send errored job alert workitem requests. WorkItemIds: {workItemIds}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task QueueSendErroredJobAlert(Job job, WorkItem workItem)
|
||||||
|
{
|
||||||
|
var request = new ErroredJobAlertRequest()
|
||||||
|
{
|
||||||
|
CurrentJob = job,
|
||||||
|
CurrentWorkItem = workItem
|
||||||
|
};
|
||||||
|
var requestList = new List<ErroredJobAlertRequest>()
|
||||||
|
{
|
||||||
|
request
|
||||||
|
};
|
||||||
|
var batchedRequest = new BatchedErroredJobAlertRequest()
|
||||||
|
{
|
||||||
|
ErroredJobAlertRequests = requestList
|
||||||
|
};
|
||||||
|
await QueueSendErroredJobAlert(batchedRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using LCU.Api.Internal.DispatcherLib.Repositories;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public class TagService : ITagService
|
||||||
|
{
|
||||||
|
private readonly ITagRepository _tagRepository;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
private readonly IJobTagService _jobTagService;
|
||||||
|
|
||||||
|
public TagService(IUnitOfWork unitOfWork,
|
||||||
|
ITagRepository tagRepository, IJobTagService jobTagService)
|
||||||
|
{
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
_tagRepository = tagRepository;
|
||||||
|
_jobTagService = jobTagService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Tag>> GetAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _tagRepository.ListAsync();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure retrieving dispatcher Tags.");
|
||||||
|
throw new ServiceException("Failure retrieving dispatcher Tags.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Tag> GetByIdAsync(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tag = await _tagRepository.FindByIdAsync(id);
|
||||||
|
ArgumentNullException.ThrowIfNull(tag);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
catch (ArgumentNullException nullEx)
|
||||||
|
{
|
||||||
|
Log.Error(nullEx, $"Failure retrieving dispatcher Tags. TagId: {id}");
|
||||||
|
throw new ArgumentNullException($"Failure retrieving dispatcher Tags. TagId: {id}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher Tags. TagId: {id}");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher Tags. TagId: {id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Tag> SaveAsync(Tag tag)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _tagRepository.AddAsync(tag);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
ArgumentNullException.ThrowIfNull(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure saving dispatcher Tag. TagId: {tag.TagId}");
|
||||||
|
throw new ServiceException($"Failure saving dispatcher Tag. TagId: {tag.TagId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Tag> UpdateAsync(Tag tag)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_tagRepository.Update(tag);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, $"SQL Failure updating dispatcher Tag. TagId: {tag.TagId}");
|
||||||
|
throw new ServiceException($"SQL Failure updating dispatcher Tag. TagId: {tag.TagId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"SQL Failure updating dispatcher Tag. TagId: {tag.TagId}");
|
||||||
|
throw new ServiceException($"SQL Failure updating dispatcher Tag. TagId: {tag.TagId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(int tagId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jobTags = await _jobTagService.GetByTagIdAsync(tagId);
|
||||||
|
if (jobTags == null || !jobTags.Any())
|
||||||
|
{
|
||||||
|
await RemoveJobTags(jobTags.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
var tag = await _tagRepository.FindByIdAsync(tagId);
|
||||||
|
ArgumentNullException.ThrowIfNull(tag);
|
||||||
|
_tagRepository.Remove(tag);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, $"SQL Failure removing dispatcher Tag. TagId: {tagId}");
|
||||||
|
throw new ServiceException($"SQL Failure removing dispatcher Tag. TagId: {tagId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure removing dispatcher Tag. TagId: {tagId}");
|
||||||
|
throw new ServiceException($"Failure removing dispatcher Tag. TagId: {tagId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> RemoveJobTags(List<JobTag> jobTags)
|
||||||
|
{
|
||||||
|
bool isSuccessful = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var jobTag in jobTags)
|
||||||
|
{
|
||||||
|
await _jobTagService.Delete(jobTag.JobTagId);
|
||||||
|
isSuccessful = true;
|
||||||
|
}
|
||||||
|
return isSuccessful;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure to remove job tags.");
|
||||||
|
throw new ServiceException("Failure to remove job tags.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+150
@@ -0,0 +1,150 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using LCU.Api.Internal.DispatcherLib.Repositories;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public class WorkItemExecutionService : IWorkItemExecutionService
|
||||||
|
{
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
private readonly IWorkItemExecutionRepository _workItemExecutionRepository;
|
||||||
|
|
||||||
|
public WorkItemExecutionService(IUnitOfWork unitOfWork,
|
||||||
|
IWorkItemExecutionRepository workItemExecutionRepository)
|
||||||
|
{
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
_workItemExecutionRepository = workItemExecutionRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItemExecution> GetByIdAsync(int id)
|
||||||
|
{
|
||||||
|
WorkItemExecution workItemExecution = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
workItemExecution = await _workItemExecutionRepository.FindByIdAsync(id);
|
||||||
|
ArgumentNullException.ThrowIfNull(workItemExecution);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher WorkItemExecution with id {id}.");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher WorkItemExecution with id {id}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return workItemExecution;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItemExecution> UpdateCompleteWorkItemExecution(WorkItemExecution workItemExecution, StatusType finalStatus)
|
||||||
|
{
|
||||||
|
workItemExecution = WorkItemExecution.SetAsFinal(workItemExecution, finalStatus);
|
||||||
|
var updatedFailedWorkItem = await UpdateAsync(workItemExecution);
|
||||||
|
return updatedFailedWorkItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItemExecution> UpdateCompleteWorkItemExecutionWithoutSave(WorkItemExecution workItemExecution, StatusType finalStatus)
|
||||||
|
{
|
||||||
|
workItemExecution = WorkItemExecution.SetAsFinal(workItemExecution, finalStatus);
|
||||||
|
var updatedFailedWorkItem = await UpdateAsyncWithoutSave(workItemExecution);
|
||||||
|
return updatedFailedWorkItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItemExecution> UpdateProcessingWorkItemExecution(WorkItemExecution workItemExecution)
|
||||||
|
{
|
||||||
|
workItemExecution.Status = StatusType.Processing;
|
||||||
|
return await UpdateAsync(workItemExecution);
|
||||||
|
}
|
||||||
|
public async Task<WorkItemExecution> UpdateFailedWorkItemExecution(WorkItemExecution workItemExecution)
|
||||||
|
{
|
||||||
|
workItemExecution.Status = StatusType.Failed;
|
||||||
|
return await UpdateAsync(workItemExecution);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItemExecution> UpdateAsync(WorkItemExecution workItemExecution)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var updatedWorkItemExecution = await UpdateAsyncWithoutSave(workItemExecution);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
|
||||||
|
return updatedWorkItemExecution;
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, $"SQL Failure updating dispatcher WorkItemExecution with id {workItemExecution.WorkItemId}");
|
||||||
|
throw new ServiceException($"SQL Failure updating dispatcher WorkItemExecution with id {workItemExecution.WorkItemId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure updating dispatcher WorkItemExecution with id {workItemExecution.WorkItemId}");
|
||||||
|
throw new ServiceException($"Failure updating dispatcher WorkItemExecution with id {workItemExecution.WorkItemId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItemExecution> UpdateAsyncWithoutSave(WorkItemExecution workItemExecution)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_workItemExecutionRepository.Update(workItemExecution);
|
||||||
|
|
||||||
|
return workItemExecution;
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, $"SQL Failure updating dispatcher WorkItemExecution with id {workItemExecution.WorkItemId}");
|
||||||
|
throw new ServiceException($"SQL Failure updating dispatcher WorkItemExecution with id {workItemExecution.WorkItemId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure updating dispatcher WorkItemExecution with id {workItemExecution.WorkItemId}");
|
||||||
|
throw new ServiceException($"Failure updating dispatcher WorkItemExecution with id {workItemExecution.WorkItemId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItemExecution> CreateWorkItemExecution(int workItemId)
|
||||||
|
{
|
||||||
|
var newWorkItemExecution = new WorkItemExecution(workItemId);
|
||||||
|
return await SaveAsync(newWorkItemExecution);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItemExecution> SaveAsync(WorkItemExecution workItemExecution)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _workItemExecutionRepository.AddAsync(workItemExecution);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, "Failure saving dispatcher WorkItemExecution", workItemExecution);
|
||||||
|
throw new ServiceException("Failure saving dispatcher WorkItemExecution");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<WorkItemExecution>> GetAllUnFinishedWorkItemExecutions(int workItemId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _workItemExecutionRepository.FindUnFinishedWorkItemExecutions(workItemId);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher WorkItemExecutions with id {workItemId}.");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher WorkItemExecutions with id {workItemId}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<WorkItemExecution>> GetAllByWorkItemId(int workItemId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _workItemExecutionRepository.FindAllByWorkItemId(workItemId);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher WorkItemExecutions with WorkItemId {workItemId}.");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher WorkItemExecutions with WorkItemId {workItemId}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
using LCU.Api.Internal.Dispatcher.Configurations;
|
||||||
|
using LCU.Api.Internal.DispatcherLib.Repositories;
|
||||||
|
using LCU.Api.Internal.DispatcherSharedLib.BuilderServices;
|
||||||
|
using LCU.Library.Internal.ApiResponse.Models;
|
||||||
|
using LCU.Library.Internal.LcuMassTransitModels;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace LCU.Api.Internal.Dispatcher.Services;
|
||||||
|
|
||||||
|
public class WorkItemService : IWorkItemService
|
||||||
|
{
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
private readonly IWorkItemRepository _workItemRepository;
|
||||||
|
private readonly IWorkItemExecutionService _workItemExecutionService;
|
||||||
|
private readonly IActionBuilderService _actionBuilderService;
|
||||||
|
private readonly ConnectionConfiguration _connectionConfiguration;
|
||||||
|
private readonly ReceiveEndpointConfiguration _completionReceiveEndpoint;
|
||||||
|
private readonly ApiConfiguration _apiConfiguration;
|
||||||
|
|
||||||
|
public WorkItemService(IUnitOfWork unitOfWork,
|
||||||
|
IWorkItemRepository workItemRepository,
|
||||||
|
IWorkItemExecutionService workItemExecutionService,
|
||||||
|
IActionBuilderService actionBuilderService,
|
||||||
|
ConnectionConfiguration connectionConfiguration,
|
||||||
|
ReceiveEndpointConfigurations queueReceiveEndpointsConfigurations,
|
||||||
|
ApiConfiguration apiConfiguration)
|
||||||
|
{
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
_workItemRepository = workItemRepository;
|
||||||
|
_workItemExecutionService = workItemExecutionService;
|
||||||
|
_actionBuilderService = actionBuilderService;
|
||||||
|
_connectionConfiguration = connectionConfiguration;
|
||||||
|
_completionReceiveEndpoint = queueReceiveEndpointsConfigurations.CompletionReceiveEndpoint;
|
||||||
|
_apiConfiguration = apiConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItem> GetByIdAsync(int id)
|
||||||
|
{
|
||||||
|
WorkItem workItem = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
workItem = await _workItemRepository.FindByIdAsync(id);
|
||||||
|
ArgumentNullException.ThrowIfNull(workItem);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher workitem with id {id}.");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher workitem with id {id}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return workItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<WorkItem>> GetByJobIdAsync(int jobId)
|
||||||
|
{
|
||||||
|
IEnumerable<WorkItem> workItems = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
workItems = await _workItemRepository.FindByJobIdAsync(jobId);
|
||||||
|
ArgumentNullException.ThrowIfNull(workItems);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher workitems with JobId {jobId}.");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher workitems with JobId {jobId}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return workItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<IEnumerable<WorkItem>> GetByJobIdWithExecutionsAsync(int jobId)
|
||||||
|
{
|
||||||
|
IEnumerable<WorkItem> workItems = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
workItems = await _workItemRepository.FindByJobIdWithExecutionsAsync(jobId,
|
||||||
|
_apiConfiguration.NumberOfWorkItemExecutions,
|
||||||
|
_apiConfiguration.NumberOfExecutionMessages);
|
||||||
|
ArgumentNullException.ThrowIfNull(workItems);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving dispatcher workitems and executions with JobId {jobId}.");
|
||||||
|
throw new ServiceException($"Failure retrieving dispatcher workitems and executions with JobId {jobId}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return workItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItem> GetFirstOrDefaultWorkItemByJobId(int jobId)
|
||||||
|
{
|
||||||
|
WorkItem workItem = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
workItem = await _workItemRepository.GetFirstOrDefaultWorkItemByJobId(jobId);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving first or default workItem with JobId: {jobId}.");
|
||||||
|
throw new ServiceException($"Failure retrieving first or default workItem with JobId: {jobId}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return workItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItem> GetNextWorkItemByJobId(int jobId, int currentWorkItemOrder)
|
||||||
|
{
|
||||||
|
WorkItem workItem = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
workItem = await _workItemRepository.GetNextWorkItemByJobId(jobId, currentWorkItemOrder);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure retrieving next workItem with JobId: {jobId}.");
|
||||||
|
throw new ServiceException($"Failure retrieving next workItem with JobId: {jobId}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return workItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ICollection<WorkItem>> PopulateActionFieldsAndUpdateList(ICollection<WorkItem> workItems)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var updatedWorkItems = new List<WorkItem>();
|
||||||
|
foreach (var workItem in workItems)
|
||||||
|
{
|
||||||
|
var newWorkItem = workItem;
|
||||||
|
newWorkItem = PopulateActionFields(newWorkItem);
|
||||||
|
newWorkItem = await UpdateAsync(newWorkItem);
|
||||||
|
updatedWorkItems.Add(newWorkItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedWorkItems;
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure populating action fields and updating dispatcher workitems.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItem> ResetCanceledJobCounter(WorkItem workItem)
|
||||||
|
{
|
||||||
|
var trackedWorkItem = await _workItemRepository.GetTrackedWorkItem(workItem.WorkItemId);
|
||||||
|
trackedWorkItem.CanceledJobCounter = 0;
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
return await _workItemRepository.FindByIdAsync(trackedWorkItem.WorkItemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItem> IncreaseCanceledJobCounter(WorkItem workItem)
|
||||||
|
{
|
||||||
|
var trackedWorkItem = await _workItemRepository.GetTrackedWorkItem(workItem.WorkItemId);
|
||||||
|
trackedWorkItem.CanceledJobCounter++;
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
return await _workItemRepository.FindByIdAsync(trackedWorkItem.WorkItemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WorkItem> UpdateAsync(WorkItem workItem)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_workItemRepository.Update(workItem);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
|
||||||
|
return workItem;
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, $"SQL Failure updating dispatcher workitem with id {workItem.WorkItemId}");
|
||||||
|
throw new ServiceException($"SQL Failure updating dispatcher workitem with id {workItem.WorkItemId}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure updating dispatcher workitem with id {workItem.WorkItemId}");
|
||||||
|
throw new ServiceException($"Failure updating dispatcher workitem with id {workItem.WorkItemId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveList(ICollection<WorkItem> workItems)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_workItemRepository.AddList(workItems);
|
||||||
|
_unitOfWork.Complete();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure saving list of workitems.");
|
||||||
|
throw new ServiceException($"Failure saving list of workitems.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteList(ICollection<WorkItem> workItems)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var workItem in workItems)
|
||||||
|
{
|
||||||
|
var workItemExecutions = await _workItemExecutionService.GetAllByWorkItemId(workItem.WorkItemId);
|
||||||
|
workItem.WorkItemExecutions = workItemExecutions.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
_workItemRepository.RemoveList(workItems);
|
||||||
|
await _unitOfWork.CompleteAsync();
|
||||||
|
}
|
||||||
|
catch (SqlException sqlException)
|
||||||
|
{
|
||||||
|
Log.Error(sqlException, $"SQL Failure removing dispatcher workitems");
|
||||||
|
throw new ServiceException($"SQL Failure removing dispatcher workitems");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception, $"Failure removing dispatcher workitems");
|
||||||
|
throw new ServiceException($"Failure removing dispatcher workitems");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorkItem PopulateActionFields(WorkItem workItem)
|
||||||
|
{
|
||||||
|
var actionBuilder = _actionBuilderService.BuildAction(workItem.ActionType);
|
||||||
|
var responseItem = CreateResponseItem(workItem.WorkItemId);
|
||||||
|
workItem = actionBuilder.AddUrlAndHttpMethod(workItem);
|
||||||
|
workItem = actionBuilder.AddWorkItemReturnResponse(workItem, responseItem);
|
||||||
|
return workItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseItem CreateResponseItem(int workItemId)
|
||||||
|
{
|
||||||
|
var dispatcherReturnResponse = new DispatcherReturnResponse
|
||||||
|
{
|
||||||
|
WorkItemId = workItemId
|
||||||
|
};
|
||||||
|
var serializeDispatcherReturnResponse = JsonConvert.SerializeObject(dispatcherReturnResponse);
|
||||||
|
|
||||||
|
var newReturnItem = new ResponseItem
|
||||||
|
{
|
||||||
|
ResponsePayload = serializeDispatcherReturnResponse,
|
||||||
|
ReturnQueueConnectionConfig = new QueueConnectionConfig()
|
||||||
|
{
|
||||||
|
Url = _connectionConfiguration.HostUrl,
|
||||||
|
Username = _connectionConfiguration.UserName,
|
||||||
|
Password = _connectionConfiguration.Password
|
||||||
|
},
|
||||||
|
ReturnQueueConsumerConfig = new QueueConsumerConfig()
|
||||||
|
{
|
||||||
|
ExchangeName = _completionReceiveEndpoint.ReceiveEndpoint
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return newReturnItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
#errors {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#errors td, #errors th {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#errors tr:nth-child(even) {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#errors th {
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#emailHeader {
|
||||||
|
color: #0054a2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#errorTableHeader {
|
||||||
|
background-color: #0054a2;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Dispatcher: Error Monitoring</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1><span id="emailHeader">Dispatcher: Error Monitoring</span></h1>
|
||||||
|
<table id="errors">
|
||||||
|
<thead id="errorTableHeader">
|
||||||
|
<tr style="height: 17px;">
|
||||||
|
<td style="max-width: 250px;font-weight: bolder;">Job Name</td>
|
||||||
|
<td style="max-width: 200;font-weight: bolder;">WorkItem Name</td>
|
||||||
|
<td style="max-width: 79px;font-weight: bolder;">JobStatus</td>
|
||||||
|
<td style="max-width: 150px;font-weight: bolder;">StartTime</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="errorRows">
|
||||||
|
{TableRows}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<tr id="errorRow" style="height: 22px;">
|
||||||
|
<td style="max-width: 250px;">{JobName}</td>
|
||||||
|
<td style="max-width: 200px;">{WorkItemName}</td>
|
||||||
|
<td style="max-width: 79px;">{JobStatus}</td>
|
||||||
|
<td style="max-width: 150px;">{StartTime}</td>
|
||||||
|
</tr>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
global using System;
|
||||||
|
global using System.Threading.Tasks;
|
||||||
|
global using System.Linq;
|
||||||
|
global using Serilog;
|
||||||
|
global using LCU.Library.Internal.Exceptions;
|
||||||
|
global using LCU.Library.Internal.DispatcherModels.Entities;
|
||||||
|
global using LCU.Library.Internal.DispatcherModels.Models;
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
{
|
||||||
|
"Primary": {
|
||||||
|
"AppName": "Dispatcher",
|
||||||
|
"SeqUrl": "http://localhost:5080",
|
||||||
|
"SeqApiKey": "9MQu0vLDwQ1LCFzGixZM",
|
||||||
|
"CorsAllowedOrigins": [
|
||||||
|
"http://localhost:3000",
|
||||||
|
"http://lcu-internal.com",
|
||||||
|
"https://lcu-internal.com",
|
||||||
|
"https://qa-api.lcu-internal.com",
|
||||||
|
"https://uat-api.lcu-internal.com",
|
||||||
|
"https://api.lcu-internal.com"
|
||||||
|
],
|
||||||
|
"SchedulersCronSchedule": "* * * * *",
|
||||||
|
"BatchErroredJobsCronSchedule": "*/15 * * * *",
|
||||||
|
"CleanUpJobsCronSchedule": "00 22 * * *",
|
||||||
|
"ProcessingLimitInMinutes": 30,
|
||||||
|
"NumberOfWorkItemExecutions": 10,
|
||||||
|
"NumberOfExecutionMessages": 20,
|
||||||
|
"CleanUpJobsDaysToDelete": -30,
|
||||||
|
"BatchErroredJobsTemplate": "./Templates/BatchErroredJobsTemplate.html",
|
||||||
|
"ErrorRowTemplate": "./Templates/ErrorRowTemplate.html",
|
||||||
|
"BatchErroredJobsSubject": "Dispatcher: Error Monitoring",
|
||||||
|
"DefaultErrorRecipient": "[email protected]",
|
||||||
|
"JobStatusesToMonitorByTimeLimit": [
|
||||||
|
"Processing",
|
||||||
|
"DeterminingOperation",
|
||||||
|
"PendingToRun"
|
||||||
|
],
|
||||||
|
"DefaultDelayMilliseconds": 5000,
|
||||||
|
"FeatureFlags": {
|
||||||
|
"RunCompletionConsumer": false,
|
||||||
|
"RunExecutionConsumer": false,
|
||||||
|
"RunJobSchedulerHostedService": false,
|
||||||
|
"RunBatchErroredJobsHostedService": false,
|
||||||
|
"RunCleanUpJobsHostedService": false,
|
||||||
|
"RunDetermineOperationConsumer": false,
|
||||||
|
"RunJobFailureConsumer": false,
|
||||||
|
"RunSendErroredJobAlertConsumer": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"FileOperationsApiConfiguration": {
|
||||||
|
"Url": "https://localhost:56595",
|
||||||
|
"MoveFileEndpoint": "/api/FileOperations/MoveFile",
|
||||||
|
"CopyFileEndpoint": "/api/FileOperations/CopyFile",
|
||||||
|
"ModifyFileEndpoint": "/api/FileOperations/ModifyFile",
|
||||||
|
"RenameFileEndpoint": "/api/FileOperations/RenameFile",
|
||||||
|
"DeleteFileEndpoint": "/api/FileOperations/DeleteFile",
|
||||||
|
"ZipDirectoryEndpoint": "/api/FileOperations/ZipDirectory",
|
||||||
|
"CreateDirectoryEndpoint": "/api/FileOperations/CreateDirectory",
|
||||||
|
"RenameDirectoryEndpoint": "/api/FileOperations/RenameDirectory",
|
||||||
|
"UnzipFileEndpoint": "/api/FileOperations/UnzipFile",
|
||||||
|
"EncryptFileDataEndpoint": "/api/FileOperations/EncryptFileData",
|
||||||
|
"DecryptFileDataEndpoint": "/api/FileOperations/DecryptFileData",
|
||||||
|
"UnzipPasswordProtectedFileEndpoint": "/api/FileOperations/UnzipPasswordProtectedFile",
|
||||||
|
"DeleteDirectoryEndpoint": "/api/FileOperations/DeleteDirectory",
|
||||||
|
"FileValidationEndpoint": "/FileValidation/QueueValidateFile",
|
||||||
|
"ExtractIndexFileDataEndpoint": "/api/IndexFile/QueueExtractIndexFileData",
|
||||||
|
"ValidateFileSizeInRange": "/FileValidation/QueueValidateFileSizeInRange",
|
||||||
|
"DeleteEmptyDirectoryEndpoint": "/api/FileOperations/DeleteEmptyDirectory"
|
||||||
|
},
|
||||||
|
"FtpApiConfiguration": {
|
||||||
|
"Url": "https://localhost:44351",
|
||||||
|
"SftpFileEndpoint": "/api/Sftp",
|
||||||
|
"QueueSftpDynamicFileEndpoint": "/api/Sftp/QueueSftpDynamicFileRequest",
|
||||||
|
"QueueSftpUploadFileEndpoint": "/api/Sftp/QueueSftpUploadRequest",
|
||||||
|
"QueueSftpDownloadFileEndpoint": "/api/Sftp/QueueSftpDownloadRequest"
|
||||||
|
},
|
||||||
|
"NotificationApiConfiguration": {
|
||||||
|
"Url": "https://qa-notifications-api.apps.azure.lcu-internal.com",
|
||||||
|
"EmailEndpoint": "/api/Email"
|
||||||
|
},
|
||||||
|
"ArchiverApiConfiguration": {
|
||||||
|
"Url": "https://localhost:44353",
|
||||||
|
"QueueArchiverEndpoint": "/QueueArchiver/QueueArchiver"
|
||||||
|
},
|
||||||
|
"BlendApiConfiguration": {
|
||||||
|
"Url": "https://localhost:54004",
|
||||||
|
"QueueDataImportEndpoint": "/api/Processing/QueueSyncDataFromBlend"
|
||||||
|
},
|
||||||
|
"DialerApiConfiguration": {
|
||||||
|
"Url": "https://localhost:1234",
|
||||||
|
"QueueDialerUploadEndpoint": "/api/DialerUpload/QueueDialerUploadRequest",
|
||||||
|
"QueueUploadDoNotCallEndpoint": "/api/UploadDoNotCall/QueueUploadDoNotCallRequest",
|
||||||
|
"QueueCreateVoAppsCampaignEndpoint": "/api/CreateVoAppsCampaign/QueueCreateVoAppsCampaignRequest",
|
||||||
|
"QueueUploadVoAppsCampaignResultsEndpoint": "/api/UploadVoAppsCampaignResults/QueueUploadVoAppsCampaignRequest",
|
||||||
|
"QueueUploadVoAppsCampaignErrorRequestEndpoint": "/api/UploadVoAppsCampaignResultsError/QueueUploadVoAppsCampaignErrorRequest"
|
||||||
|
},
|
||||||
|
"DocumentIngestorApiConfiguration": {
|
||||||
|
"Url": "https://localhost:7139",
|
||||||
|
"QueueDocumentIngestorEndpoint": "/DocumentIngestor/QueueDocumentIngestor",
|
||||||
|
"QueueDocumentBatchIngestionEndpoint": "/BatchIngestion/QueueBatchIngestion",
|
||||||
|
"QueueIngestorPdfPartitionerEndpoint": "/PdfPartitioner/QueueIngestorPdfPartitioner",
|
||||||
|
"QueueMemberDocumentNotifierEndpoint": "/MemberDocumentNotifier/QueueMemberDocumentNotifier",
|
||||||
|
"QueueIngestionFolderCleanUpEndpoint": "/IngestionFolderCleanUp/QueueIngestionFolderCleanUp",
|
||||||
|
"QueueProcessStatusTypeEmailRequestEndpoint": "/QueueProcessStatusTypeEmail/QueueProcessStatusTypeEmailRequest"
|
||||||
|
},
|
||||||
|
"DocumentTypeCleanupApiConfiguration": {
|
||||||
|
"Url": "https://localhost:7066",
|
||||||
|
"QueueDocumentRetentionCleanupEndpoint": "/DocumentTypeCleanup/QueueDocumentRetentionCleanup"
|
||||||
|
},
|
||||||
|
"HistoricalDocumentIngestorApiConfiguration": {
|
||||||
|
"Url": "https://localhost:44346",
|
||||||
|
"QueueDocumentIngestorEndpoint": "/DocumentIngestor/QueueDocumentIngestor",
|
||||||
|
"QueueDocumentBatchIngestionEndpoint": "/BatchIngestion/QueueBatchIngestion",
|
||||||
|
"QueueIngestorPdfPartitionerEndpoint": "/PdfPartitioner/QueueIngestorPdfPartitioner"
|
||||||
|
},
|
||||||
|
"RemoteDepositCaptureApiConfiguration": {
|
||||||
|
"Url": "https://localhost:44305",
|
||||||
|
"QueueRemoteDepositCaptureXmlEndpoint": "/api/RemoteDepositCaptureXml/QueueRemoteDepositCaptureXmlRequest"
|
||||||
|
},
|
||||||
|
"DataCleanUpApiConfiguration": {
|
||||||
|
"Url": "https://localhost:44357",
|
||||||
|
"QueueDataCleanUpEndpoint": "/QueueDataCleanUp/QueueDataCleanUp",
|
||||||
|
"QueueUpdatePersonForCheckHoldsEndpoint": "/QueueDataCleanUp/QueueUpdatePersonForCheckHolds",
|
||||||
|
"QueueNcoaUpdateEndpoint": "/QueueDataCleanUp/QueueUpdateNcoaProcess",
|
||||||
|
"QueueUpdateFannieMaeMortgageEndpoint": "/QueueDataCleanUp/QueueUpdateFannieMaeMortgage",
|
||||||
|
"QueueIndirectFundingEndpoint": "/QueueDataCleanUp/QueueIndirectFunding"
|
||||||
|
},
|
||||||
|
"NoticeProcessorApiConfiguration": {
|
||||||
|
"Url": "https://localhost:7275",
|
||||||
|
"QueueExtractEndpoint": "/QueueExtractRequest/RunNoticeProcessorByExtractRequestNow",
|
||||||
|
"QueueProcessingEmailSummaryEndpoint": "/QueueProcessingEmailRequest/RunProcessingEmailRequestNow"
|
||||||
|
},
|
||||||
|
"MomBotApiConfiguration": {
|
||||||
|
"Url": "https://localhost:5001",
|
||||||
|
"QueueMomBotEndpoint": "/api/QueueMomBot"
|
||||||
|
},
|
||||||
|
"RealTimeAlertsApiConfiguration": {
|
||||||
|
"Url": "https://localhost:59626",
|
||||||
|
"QueueInitiateRealTimeAlertsEndpoint": "/api/TransactionAlerts/QueueInitiateTransactionAlertsRequest",
|
||||||
|
"QueueErroredTransactionCleanUpEndpoint": "/api/TransactionAlerts/QueueInitiateErroredTransactionCleanUpRequest"
|
||||||
|
},
|
||||||
|
"RepoApiConfiguration": {
|
||||||
|
"Url": "https://localhost:7066",
|
||||||
|
"AccountBatchEndpoint": "/api/RepoBatchProcessing/QueueRepoBatchRequest"
|
||||||
|
},
|
||||||
|
"ConnectionsConfiguration": {
|
||||||
|
"SQL": {
|
||||||
|
"DispatcherDb": "data source=localhost;initial catalog=Dispatcher;Integrated Security=SSPI;MultipleActiveResultSets=True;App=EntityFramework;Encrypt=false",
|
||||||
|
"SQLOptions": {
|
||||||
|
"MaxRetryCount": 5,
|
||||||
|
"MaxRetryDelay": "0.00:05:00",
|
||||||
|
"ErrorNumbersToAdd": [],
|
||||||
|
"EnableDetailedErrors": true,
|
||||||
|
"CommandTimeout": 60
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DispatcherRequestBuilderConfiguration": {
|
||||||
|
"Url": "https://localhost:44380/api/Ingestion"
|
||||||
|
},
|
||||||
|
"DispatcherCompletionConsumer": {
|
||||||
|
"QueueName": "LCU.Queue.Dispatcher.Completion",
|
||||||
|
"ExchangeName": "LCU.Dispatcher.Direct",
|
||||||
|
"Subject": "LCU.Queue.Dispatcher.Completion"
|
||||||
|
},
|
||||||
|
"ConnectionConfiguration": {
|
||||||
|
"HostUrl": "localhost",
|
||||||
|
"username": "guest",
|
||||||
|
"password": "guest"
|
||||||
|
},
|
||||||
|
"ExecutionReceiveEndpoint": {
|
||||||
|
"ReceiveEndpoint": "LCU.Dispatcher.Execution",
|
||||||
|
"ReceiveEndpointType": "exchange",
|
||||||
|
"PrefetchLimit": 1
|
||||||
|
},
|
||||||
|
"CompletionReceiveEndpoint": {
|
||||||
|
"ReceiveEndpoint": "LCU.Dispatcher.Completion",
|
||||||
|
"ReceiveEndpointType": "exchange",
|
||||||
|
"PrefetchLimit": 1
|
||||||
|
},
|
||||||
|
"DetermineOperationReceiveEndpoint": {
|
||||||
|
"ReceiveEndpoint": "LCU.Dispatcher.DetermineOperation",
|
||||||
|
"ReceiveEndpointType": "exchange",
|
||||||
|
"PrefetchLimit": 1
|
||||||
|
},
|
||||||
|
"JobFailureReceiveEndpoint": {
|
||||||
|
"ReceiveEndpoint": "LCU.Dispatcher.JobFailure",
|
||||||
|
"ReceiveEndpointType": "exchange",
|
||||||
|
"PrefetchLimit": 1
|
||||||
|
},
|
||||||
|
"SendErroredJobAlertReceiveEndpoint": {
|
||||||
|
"ReceiveEndpoint": "LCU.Dispatcher.SendErroredJobAlert",
|
||||||
|
"ReceiveEndpointType": "exchange",
|
||||||
|
"PrefetchLimit": 100
|
||||||
|
},
|
||||||
|
"ApiSecurityConfiguration": {
|
||||||
|
"ApiKeyAuthenticationFailureBehavior": "Error",
|
||||||
|
"ExternalApiKeySet": {
|
||||||
|
"DispatcherHostedServiceClient": "VALUE-IN-VAULT"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IncentiveManagerConfiguration": {
|
||||||
|
"Url": "https://localhost:20001/api",
|
||||||
|
"QueueCreditCardImportEndpoint": "/ActivityImport/QueueCreditCardImportRequest",
|
||||||
|
"QueueVehicleImportEndpoint": "/ActivityImport/QueueIndirectVehicleImportRequest",
|
||||||
|
"QueueActionCreditCardEndpoint": "/ActivityImport/QueueActionCreditCardRequest",
|
||||||
|
"QueueActionVehicleEndpoint": "/ActivityImport/QueueActionVehicleRequest",
|
||||||
|
"QueueAlliedGapMbpEndpoint": "/ActivityImport/QueueAlliedGapMbpRequest",
|
||||||
|
"QueueIncentiveProcessEndpoint": "/Processing/QueueIncentiveProcess",
|
||||||
|
"QueueNightlyIncentiveProcessEndpoint": "/Processing/QueueNightlyIncentiveProcess",
|
||||||
|
"QueuePayoutProcessEndpoint": "/Processing/QueuePayoutProcess",
|
||||||
|
"QueueReportingProcessEndpoint": "/Processing/QueueReportingRequest",
|
||||||
|
"QueueGoalDispersalProcessEndpoint": "/Processing/QueueGoalDispersalSyncProcess",
|
||||||
|
"QueueDayforceProcessEndpoint": "/Processing/QueueDayforceSyncProcess"
|
||||||
|
},
|
||||||
|
"FileUpdaterApiConfiguration": {
|
||||||
|
"Url": "https://localhost:56076",
|
||||||
|
"QueueIndexFieldMapper": "/api/FileUpdater/QueueIndexFieldMapper"
|
||||||
|
},
|
||||||
|
"TemenosNotificationApiConfiguration": {
|
||||||
|
"Url": "https://localhost:7076",
|
||||||
|
"QueueDealerAchNotification": "/api/QueueDealerAchNotification"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"defaultProvider": "cdnjs",
|
||||||
|
"libraries": []
|
||||||
|
}
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Enums;
|
||||||
|
using LCU.Api.Internal.DispatcherSharedLib;
|
||||||
|
using LCU.Api.Internal.DispatcherSharedLib.BuilderServices;
|
||||||
|
using LCU.Library.Internal.FileOperationsModels.Models;
|
||||||
|
using LCU.Library.Internal.LcuHttpClient;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LCU.Api.Internal.DispatcherIntegrationTests;
|
||||||
|
|
||||||
|
public class DispatcherRequestBuilderTest
|
||||||
|
{
|
||||||
|
private readonly IServiceCollection services;
|
||||||
|
private readonly IRequestBuilderService _requestBuilder;
|
||||||
|
private const string TestPath1 = "\\\\zeus\\DataIntegrationsQA\\Test_FileOperationsApi_Folder_1";
|
||||||
|
private const string TestPath2 = "\\\\zeus\\DataIntegrationsQA\\Test_FileOperationsApi_Folder_1";
|
||||||
|
|
||||||
|
public DispatcherRequestBuilderTest()
|
||||||
|
{
|
||||||
|
services = new ServiceCollection();
|
||||||
|
|
||||||
|
var builder = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(Path.Combine(AppContext.BaseDirectory))
|
||||||
|
.AddJsonFile("appsettings.integration.json");
|
||||||
|
var config = builder.Build();
|
||||||
|
services.AddDispatcherServices(config);
|
||||||
|
services.AddLCUHttpServices();
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
_requestBuilder = serviceProvider.GetService<IRequestBuilderService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
// !!!!!! In order to run these test you will need the following set up locally. !!!!!!
|
||||||
|
// 1.) Local Dispatcher Database.
|
||||||
|
// 2.) Local docker with Rabbit installed and running.
|
||||||
|
// 3.) Local instance of Dispatcher Api running along with other apis being tested (eg. FileOperationsApi).
|
||||||
|
// Which will also need to be using local Rabbit queues.
|
||||||
|
// 4.) Can use the the following two locations on QA for file operation testing.
|
||||||
|
// - \\zeus\DataIntegrationsQA\Test_FileOperationsApi_Folder_1
|
||||||
|
// - \\zeus\DataIntegrationsQA\Test_FileOperationsApi_Folder_2
|
||||||
|
// !!!!!! ---------------------------------------------------------------------- !!!!!!
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Ignore("Can only be run locally.")]
|
||||||
|
public async Task Test_CreateDirectory()
|
||||||
|
{
|
||||||
|
//Create Job
|
||||||
|
var job = new JobDetails()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
RequestingApplication = "Dispatcher Integration Test",
|
||||||
|
CreatedBy = "Dispatcher Integration Test",
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
NextRunTime = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
_requestBuilder.CreateJob(job);
|
||||||
|
|
||||||
|
//Create WorkItem
|
||||||
|
var createDirectoryRequest = new CreateDirectoryRequest()
|
||||||
|
{
|
||||||
|
DirectoryName = "FirstDispatcherIntegrationTest",
|
||||||
|
DirectoryPath = TestPath1
|
||||||
|
};
|
||||||
|
|
||||||
|
var workItemDetailsCreateDirectory = new WorkItemDetails()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Order = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
_requestBuilder.AddWorkItem(createDirectoryRequest, workItemDetailsCreateDirectory, ActionType.CreateDirectory);
|
||||||
|
|
||||||
|
//Send
|
||||||
|
var result = await _requestBuilder.SendDispatcherRequest();
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Ignore("Can only be run locally.")]
|
||||||
|
public async Task Test_RenameDirectory()
|
||||||
|
{
|
||||||
|
//Create Job
|
||||||
|
var job = new JobDetails()
|
||||||
|
{
|
||||||
|
Name = "Test - Test_RenameDirectory",
|
||||||
|
RequestingApplication = "Dispatcher Integration Test - Test_RenameDirectory Builder",
|
||||||
|
CreatedBy = "Dispatcher Integration Test",
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
NextRunTime = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
_requestBuilder.CreateJob(job);
|
||||||
|
|
||||||
|
//Create WorkItem
|
||||||
|
var renameDirectoryRequest = new RenameDirectoryRequest()
|
||||||
|
{
|
||||||
|
SourceDirectoryName = "FirstDispatcherIntegrationTest",
|
||||||
|
SourceDirectoryPath = TestPath1,
|
||||||
|
NewDirectoryName = "RenameDispatcherIntegrationTest2"
|
||||||
|
};
|
||||||
|
|
||||||
|
var workItemDetailsCreateDirectory = new WorkItemDetails()
|
||||||
|
{
|
||||||
|
Name = "Test - Test_RenameDirectory",
|
||||||
|
Order = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
_requestBuilder.AddWorkItem(renameDirectoryRequest, workItemDetailsCreateDirectory, ActionType.RenameDirectory);
|
||||||
|
|
||||||
|
//Send
|
||||||
|
var result = await _requestBuilder.SendDispatcherRequest();
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="appsettings.integration.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="appsettings.integration.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Moq" Version="4.13.0" />
|
||||||
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\LCU.Api.Internal.DispatcherLib\LCU.Api.Internal.DispatcherLib.csproj" />
|
||||||
|
<ProjectReference Include="..\LCU.Api.Internal.DispatcherSharedLib\LCU.Api.Internal.DispatcherSharedLib.csproj" />
|
||||||
|
<ProjectReference Include="..\LCU.Api.Internal.Dispatcher\LCU.Api.Internal.Dispatcher.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"Serilog": {
|
||||||
|
"LevelSwitches": { "$controlSwitch": "Information" },
|
||||||
|
"MinimumLevel": {
|
||||||
|
"ControlledBy": "$controlSwitch",
|
||||||
|
"Override": {
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"System": "Warning",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"WriteTo": [
|
||||||
|
{
|
||||||
|
"Name": "Seq",
|
||||||
|
"Args": {
|
||||||
|
"serverUrl": "https://qa-seq.lcu-internal.com",
|
||||||
|
"apiKey": "VfIDjFsJC1zLalKeesnu",
|
||||||
|
"controlLevelSwitch": "$controlSwitch"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
|
||||||
|
"Properties": {
|
||||||
|
"Application": "Dispatcher",
|
||||||
|
"Environment": "Debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"Primary": {
|
||||||
|
"AttemptsLimit": 2,
|
||||||
|
"SchedulersCronSchedule": "* * * * *",
|
||||||
|
"Apis": {
|
||||||
|
"FileOperationApi": {
|
||||||
|
"MoveFile": "https://localhost:44385/api/FileOperations/MoveFile",
|
||||||
|
"CopyFile": "https://localhost:44385/api/FileOperations/CopyFile",
|
||||||
|
"RenameFile": "https://localhost:44385/api/FileOperations/RenameFile",
|
||||||
|
"DeleteFile": "https://localhost:44385/api/FileOperations/DeleteFile",
|
||||||
|
"ZipDirectory": "https://localhost:44385/api/FileOperations/ZipDirectory",
|
||||||
|
"CreateDirectory": "https://localhost:44385/api/FileOperations/CreateDirectory",
|
||||||
|
"RenameDirectory": "https://localhost:44385/api/FileOperations/RenameDirectory"
|
||||||
|
},
|
||||||
|
"FtpApi": {
|
||||||
|
"SftpFile": "https://localhost:44383/api/Sftp"
|
||||||
|
},
|
||||||
|
"NotificationApi": {
|
||||||
|
"Email": "http://localhost:54954/api/Email"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Connections": {
|
||||||
|
"SQL": {
|
||||||
|
"DispatcherDb": "data source=localhost;initial catalog=Dispatcher;Integrated Security=SSPI;MultipleActiveResultSets=True;App=EntityFramework"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DispatcherConfig": {
|
||||||
|
"Url": "https://localhost:44380/api/QueueIngestion"
|
||||||
|
},
|
||||||
|
"DispatcherIngestionConsumer": {
|
||||||
|
"QueueName": "LCU.Queue.Dispatcher.Ingestion",
|
||||||
|
"ExchangeName": "LCU.Dispatcher.Direct",
|
||||||
|
"Subject": "LCU.Queue.Dispatcher.Ingestion"
|
||||||
|
},
|
||||||
|
"ErroredDispatcherIngestionConsumer": {
|
||||||
|
"QueueName": "LCU.Queue.Dispatcher.Ingestion.Errored",
|
||||||
|
"ExchangeName": "LCU.Dispatcher.Direct",
|
||||||
|
"Subject": "LCU.Queue.Dispatcher.Ingestion.Errored"
|
||||||
|
},
|
||||||
|
"DispatcherExecutionConsumer": {
|
||||||
|
"QueueName": "LCU.Queue.Dispatcher.Execution",
|
||||||
|
"ExchangeName": "LCU.Dispatcher.Direct",
|
||||||
|
"Subject": "LCU.Queue.Dispatcher.Execution"
|
||||||
|
},
|
||||||
|
"DispatcherCompletionConsumer": {
|
||||||
|
"QueueName": "LCU.Queue.Dispatcher.Completion",
|
||||||
|
"ExchangeName": "LCU.Dispatcher.Direct",
|
||||||
|
"Subject": "LCU.Queue.Dispatcher.Completion"
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Entities;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models;
|
||||||
|
namespace LCU.Api.Internal.DispatcherLib.Extensions.Enumerables;
|
||||||
|
|
||||||
|
public static class StatusTypeMatch
|
||||||
|
{
|
||||||
|
public static IQueryable<Job> FilterByStatusType(this IQueryable<Job> jobs, Filter filter)
|
||||||
|
{
|
||||||
|
if (filter?.StatusFilters == null || !filter.StatusFilters.Any())
|
||||||
|
{
|
||||||
|
return jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
var enabledStatuses = filter.StatusFilters.Where(statusFilter => statusFilter.IsEnabled).Select(j => j.JobStatus).ToList();
|
||||||
|
|
||||||
|
if (!enabledStatuses.Any())
|
||||||
|
{
|
||||||
|
return jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jobs.Where(job => enabledStatuses.Contains(job.JobStatus));
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Entities;
|
||||||
|
namespace LCU.Api.Internal.DispatcherLib.Extensions.Queryables;
|
||||||
|
|
||||||
|
public static class ContainsTagIdQueryable
|
||||||
|
{
|
||||||
|
public static IQueryable<Job> FilterOnTagId(this IQueryable<Job> queryable, Filter filter)
|
||||||
|
{
|
||||||
|
if (filter?.TagIdFilters == null || !filter.TagIdFilters.Any())
|
||||||
|
{
|
||||||
|
return queryable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryable.Where(job => job.JobTags.Any(jobTag => filter.TagIdFilters.Contains(jobTag.TagId)));
|
||||||
|
}
|
||||||
|
}
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Entities;
|
||||||
|
namespace LCU.Api.Internal.DispatcherLib.Extensions.Queryables;
|
||||||
|
|
||||||
|
public static class IncludeAllQueryable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Since this runs as Split Query, the OrderyByDescending only works during the querying but not in our result set.
|
||||||
|
/// OrderbyDescending on WorkItemExecutions will have to be done prior to use if need be.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queryable"></param>
|
||||||
|
/// <param name="numberOfWorkItems"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IQueryable<Job> IncludeAll(this IQueryable<Job> queryable, int numberOfWorkItemExecutions, int numberOfExecutionMessages)
|
||||||
|
{
|
||||||
|
return queryable.Include(job => job.WorkItems)
|
||||||
|
.ThenInclude(wi => wi.WorkItemExecutions
|
||||||
|
.OrderByDescending(workItemExecution => workItemExecution.StartedTimestamp)
|
||||||
|
.Take(numberOfWorkItemExecutions))
|
||||||
|
.ThenInclude(workItemExecution => workItemExecution.ExecutionMessages
|
||||||
|
.OrderByDescending(executionMessage => executionMessage.MessageDate)
|
||||||
|
.Take(numberOfExecutionMessages))
|
||||||
|
.Include(job => job.JobErrorRecipients)
|
||||||
|
.Include(jobTag => jobTag.JobTags)
|
||||||
|
.ThenInclude(jobTag => jobTag.Tag)
|
||||||
|
.AsSplitQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<Job> IncludeErrorRecipients(this IQueryable<Job> queryable)
|
||||||
|
{
|
||||||
|
return queryable.Include(job => job.JobErrorRecipients);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<WorkItem> IncludeAll(this IQueryable<WorkItem> queryable)
|
||||||
|
{
|
||||||
|
return queryable.Include(workItem => workItem.WorkItemExecutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<WorkItem> IncludeAll(this IQueryable<WorkItem> queryable, int numberOfWorkItemExecutions, int numberOfExecutionMessages)
|
||||||
|
{
|
||||||
|
return queryable.Include(wi => wi.WorkItemExecutions
|
||||||
|
.OrderByDescending(workItemExecution => workItemExecution.StartedTimestamp)
|
||||||
|
.Take(numberOfWorkItemExecutions))
|
||||||
|
.ThenInclude(workItemExecution => workItemExecution.ExecutionMessages
|
||||||
|
.OrderByDescending(executionMessage => executionMessage.MessageDate)
|
||||||
|
.Take(numberOfExecutionMessages));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<JobTag> IncludeAll(this IQueryable<JobTag> queryable)
|
||||||
|
{
|
||||||
|
return queryable.Include(jobTag => jobTag.Tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<WorkItemExecution> IncludeAll(this IQueryable<WorkItemExecution> queryable)
|
||||||
|
{
|
||||||
|
return queryable.Include(workItemExecution => workItemExecution.ExecutionMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<CleanUpJobSetting> IncludeAll(this IQueryable<CleanUpJobSetting> queryable)
|
||||||
|
{
|
||||||
|
return queryable.Include(cleanUpJobSetting => cleanUpJobSetting.CleanUpJobTags)
|
||||||
|
.ThenInclude(cleanUpJobTag => cleanUpJobTag.Tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<CleanUpJobTag> IncludeAll(this IQueryable<CleanUpJobTag> queryable)
|
||||||
|
{
|
||||||
|
return queryable.Include(cleanUpJobTag => cleanUpJobTag.Tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Entities;
|
||||||
|
namespace LCU.Api.Internal.DispatcherLib.Extensions.Queryables;
|
||||||
|
|
||||||
|
public static class JobDescriptionQueryable
|
||||||
|
{
|
||||||
|
public static IQueryable<Job> FilterJobDescription(this IQueryable<Job> queryable, Filter filter)
|
||||||
|
{
|
||||||
|
if (filter?.JobDescription == null)
|
||||||
|
{
|
||||||
|
return queryable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryable.Where(job => job.Description.Contains(filter.JobDescription));
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Entities;
|
||||||
|
namespace LCU.Api.Internal.DispatcherLib.Extensions.Queryables;
|
||||||
|
|
||||||
|
public static class JobNameQueryable
|
||||||
|
{
|
||||||
|
public static IQueryable<Job> FilterJobName(this IQueryable<Job> queryable, Filter filter)
|
||||||
|
{
|
||||||
|
if (filter?.JobName == null)
|
||||||
|
{
|
||||||
|
return queryable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryable.Where(job => job.Name.Contains(filter.JobName));
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Models;
|
||||||
|
using LCU.Library.Internal.DispatcherModels.Entities;
|
||||||
|
namespace LCU.Api.Internal.DispatcherLib.Extensions.Queryables;
|
||||||
|
|
||||||
|
public static class NextRunTimeGreaterThanDateQueryable
|
||||||
|
{
|
||||||
|
public static IQueryable<Job> FilterNextRunTimeGreaterThanDate(this IQueryable<Job> queryable, Filter filter)
|
||||||
|
{
|
||||||
|
if (filter?.NextRuntimeGreaterThanDate == null)
|
||||||
|
{
|
||||||
|
return queryable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryable.Where(job => job.NextRunTime >= filter.NextRuntimeGreaterThanDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user