5 Commits

Author SHA1 Message Date
538b1f817e Align .editorconfig files
When debugging only
app.Services.GetRequiredService<IPCRBService>();

Injected AppSettings instead of using GetEnvironmentVariable at Services level

Get ready to use VSCode IDE
2024-12-03 12:23:56 -07:00
89790f4fc1 PCRB webassembly 2024-11-15 09:28:25 -07:00
9b7e3ef897 MRB webassembly 2024-11-15 09:28:21 -07:00
ba8d92ea01 DRY 2024-08-05 07:39:40 -07:00
603052d7e5 Clean up training assignments through report 2024-08-02 15:06:59 -07:00
148 changed files with 19690 additions and 1158 deletions

View File

@ -1,74 +0,0 @@
# EditorConfig is awesome:http://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
# Don't use tabs for indentation.
# (Please don't specify an indent_size here; that has too many unintended consequences.)
indent_style = space
charset = utf-8
# Where supported, trim trailing whitespace on all lines.
trim_trailing_whitespace = true
# Where supported (e.g. in VS Code but not VS), add a final newline to files.
insert_final_newline = true
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
dotnet_sort_system_directives_first = true:warning
# Xml project files
[*.{*proj,vcxproj.filters,projitems}]
indent_size = 2
# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct,tasks,xml,yml}]
indent_size = 2
# JSON files
[*.json]
indent_size = 2
# PowerShell
[*.{ps1,psm1}]
indent_size = 4
# Shell
[*.sh]
indent_size = 4
end_of_line = lf
# Dotnet code style settings:
[*.cs]
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
# Don't use this. qualifier
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
# use int x = .. over Int32
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
# use int.MaxValue over Int32.MaxValue
dotnet_style_predefined_type_for_member_access = true:suggestion
# Require var all the time.
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = false:suggestion
csharp_style_var_elsewhere = false:suggestion
# Disallow throw expressions.
csharp_style_throw_expression = false:suggestion
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true

20
.gitignore vendored
View File

@ -337,3 +337,23 @@ ASALocalRun/
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.env
/Fab2ApprovalMKLink/App_Start
/Fab2ApprovalMKLink/Controllers
/Fab2ApprovalMKLink/DMO
/Fab2ApprovalMKLink/Jobs
/Fab2ApprovalMKLink/JobSchedules
/Fab2ApprovalMKLink/Misc
/Fab2ApprovalMKLink/Models
/Fab2ApprovalMKLink/PdfGenerator
/Fab2ApprovalMKLink/Utilities
/Fab2ApprovalMKLink/ViewModels
/Fab2ApprovalMKLink/.vscode/.UserSecrets
/Fab2ApprovalSystem/.vscode/.UserSecrets
/Fab2ApprovalTests/.vscode/.UserSecrets
/MesaFabApproval.API/.vscode/.UserSecrets
/MesaFabApproval.Client/.vscode/.UserSecrets
/MesaFabApproval.Shared/.vscode/.UserSecrets

41
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,41 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console) - Fab2ApprovalSystem",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "Fab2ApprovalSystem-build",
"program": "${workspaceFolder}/Fab2ApprovalSystem/bin/Debug/net8.0/win-x64/Fab2ApprovalSystem.dll",
"args": [],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false
},
{
"name": ".NET Core Launch (console) - MesaFabApproval.API",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "MesaFabApproval.API-build",
"program": "${workspaceFolder}/MesaFabApproval.API/bin/Debug/net8.0/MesaFabApproval.API.dll",
"args": [],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
},
{
"type": "node",
"request": "launch",
"name": "node Launch Current Opened File",
"program": "${file}"
}
]
}

424
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,424 @@
{
"[markdown]": {
"editor.wordWrap": "off"
},
"files.exclude": {
"**/.git": false,
"**/node_modules": true
},
"files.watcherExclude": {
"**/node_modules": true
},
"cSpell.words": [
"abutton",
"accessibilities",
"accodingly",
"acknowledgmentby",
"Acks",
"actionsheet",
"Additonal",
"Addtional",
"againm",
"agendaview",
"Antlr",
"Appover",
"Appprrovers",
"Approvalog",
"Aprovers",
"Aproverslist",
"asax",
"aspnetmvc",
"Assignedto",
"Atachments",
"Attachemnt",
"Attachemnts",
"Attchment",
"auditee",
"Auditee",
"Auditees",
"automaically",
"Autthorized",
"beacuase",
"beforeunload",
"Belguim",
"bfound",
"bgcolor",
"Bies",
"binded",
"blackbackground",
"blackhover",
"blackpressed",
"blueenergy",
"blueopal",
"buttongroup",
"BYMRB",
"bytesgot",
"CAID",
"casection",
"CAXXXX",
"CCPCR",
"CCPCRB",
"cellspacing",
"Cheeso's",
"chkbx",
"Clib",
"colorpicker",
"columnmenu",
"columnsreorder",
"columnsresize",
"comming",
"Containmen",
"Copmments",
"correctiv",
"Correctivet",
"Creat",
"currentd",
"Cyle",
"dadada",
"darkbluehover",
"darkbluepressed",
"darkred",
"datafields",
"datasource",
"dataviz",
"datepicker",
"datetimepicker",
"dayview",
"Deletet",
"Delgation",
"DENITED",
"Deparmtent",
"departmentids",
"Descirption",
"devprog",
"dfeffc",
"Disp",
"Dispo",
"Dispoitio",
"Dispos",
"Dispositon",
"Dispostion",
"Dispostions",
"dispotypevalidation",
"Docbase",
"Documentum",
"Documetum",
"dont",
"downlaoded",
"draganddrop",
"dragcancel",
"dropdownlist",
"Eamils",
"ECNPCRB",
"Ecns",
"edmx",
"EECN",
"emai",
"emailparams",
"Emergrncy",
"energyblue",
"Eran",
"Esql",
"ETECN",
"EXCELOPENXML",
"existinglocation",
"Expando",
"extrafield",
"fadc",
"fbec",
"fcfdfd",
"fdff",
"fece",
"feeebd",
"ffdc",
"ffdd",
"fieldset",
"FILIPE",
"filtermenu",
"Fldr",
"flintstone",
"FLOWLOCS",
"FMEA",
"ftplib",
"FTPSPN",
"GETDATE",
"gitea",
"globaldocudms",
"glyphicons",
"groupable",
"Guids",
"halflings",
"Hexsize",
"highcontrast",
"Hmac",
"holdsteps",
"hostspecific",
"icenium",
"IECN",
"imagebrowser",
"IMRB",
"Infineon",
"Insertd",
"inverseicons",
"IPCRB",
"ISADMIN",
"islast",
"ISNULL",
"ITAR",
"jquery",
"jqueryval",
"jqwidgets",
"jqxbuttongroup",
"jqxbuttons",
"jqxcalendar",
"jqxchart",
"jqxcheckbox",
"jqxcolorpicker",
"jqxcombobox",
"jqxcore",
"jqxdata",
"jqxdatatable",
"jqxdatetimeinput",
"jqxdocking",
"jqxdockpanel",
"jqxdragdrop",
"jqxdropdownbutton",
"jqxdropdownlist",
"jqxexpander",
"jqxgauge",
"jqxgrid",
"jqxinput",
"jqxknockout",
"jqxlistbox",
"jqxlistmenu",
"jqxmaskedinput",
"jqxmenu",
"jqxnavigationbar",
"jqxnumberinput",
"jqxpanel",
"jqxpasswordinput",
"jqxprogressbar",
"jqxradiobutton",
"jqxrangeselector",
"jqxrating",
"jqxresponse",
"jqxscrollbar",
"jqxscrollview",
"jqxslider",
"jqxsplitter",
"jqxswitchbutton",
"jqxtabs",
"jqxtooltip",
"jqxtouch",
"jqxtree",
"jqxtreegrid",
"jqxtreemap",
"jqxvalidator",
"jqxwindow",
"kendogridcustom",
"kendoui",
"labelelement",
"labelledby",
"Leanred",
"lightgray",
"linkbutton",
"Linq",
"Listdiv",
"listview",
"Lnks",
"localfilename",
"loclist",
"logis",
"logtext",
"loopmis",
"lotdispo",
"LOTDISPSITION",
"Lotfile",
"lotlist",
"lotstatusoption",
"LTRIM",
"MADUREIRA",
"mailrelay",
"MDTM",
"meego",
"meetingid",
"menubutton",
"mesafi",
"metroblack",
"metrodark",
"miliseconds",
"modalview",
"modernizr",
"Modernizr",
"monthview",
"MRBIs",
"Mrbs",
"msecs",
"multipleextended",
"Navigatable",
"nbsp",
"newbase",
"newchange",
"newdi",
"newfilename",
"newsource",
"Newtonsoft",
"notications",
"Notifcation",
"Notifyf",
"NTLM",
"Nullcc",
"numerictextbox",
"objdata",
"OCAP",
"occured",
"odata",
"oldfilename",
"OLHOLD",
"onclick",
"onmousemove",
"OPDESC",
"OPENQUERY",
"Oper",
"operationslist",
"Orginator",
"Originatorname",
"Ouellette",
"Owin",
"pageable",
"Pageable",
"panelbar",
"parentid",
"parminput",
"parms",
"Parms",
"particula",
"pasv",
"PASV",
"PATINDEX",
"PCRB",
"PCRBID",
"pcrvalues",
"pdbonly",
"Preventitive",
"preventivet",
"Prevetative",
"proces",
"Processedl",
"procs",
"productfamilies",
"progess",
"progressbar",
"qrcode",
"Quanityt",
"rangebar",
"Recep",
"Recepient",
"recieved",
"recordlock",
"remotefilename",
"reorderable",
"reportform",
"reportslist",
"reportslistdiv",
"Reqquired",
"Reqs",
"Requiest",
"Responsibles",
"RETR",
"Revisioing",
"Revisioned",
"Revison",
"rgba",
"rkotian",
"RNFR",
"RNTO",
"Roless",
"roundbg",
"RTRIM",
"SAMDB",
"scroller",
"scrollview",
"seleced",
"selectionlog",
"Selectpart",
"sess",
"Sfisharepoint",
"shinyblack",
"showpassword",
"SIGNON",
"simpleparser",
"slddrw",
"sldprt",
"sortasc",
"sortascbutton",
"sortdesc",
"sortdescbutton",
"sortremove",
"sparkline",
"splitview",
"SPNMRB",
"SPNPDB",
"SSRS",
"Sssign",
"Staus",
"stylesheet",
"Submited",
"subrole",
"subroles",
"Succefully",
"Succesfully",
"sucessfully",
"SURP",
"Swashbuckle",
"SWRN",
"tabindex",
"tabstrip",
"Tahoma",
"taskcompleted",
"Tasklist",
"Taveler",
"TECN",
"TECNs",
"TEMIRWAP",
"tempecd",
"tempimplement",
"templabel",
"tempvalue",
"TEMSA",
"timepicker",
"Tobe",
"Toplevel",
"Totrav",
"trainingby",
"Traininglist",
"traininglistdiv",
"transanction",
"Trav",
"Traveller",
"Traverler",
"TRAVLELER",
"Travler",
"TREEVIEW",
"trigerred",
"ttinclude",
"Uhandled",
"Updat",
"Uplaod",
"Upto",
"userevents",
"userids",
"userlist",
"Validatable",
"valueelement",
"Variabls",
"Verdana",
"vgrid",
"viewmodel",
"vsdoc",
"whethere",
"windowsphone",
"Winsock",
"worlflow"
]
}

500
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,500 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "MSBuild",
"command": "C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/MSBuild/Current/Bin/MSBuild.exe",
"type": "process",
"args": [
"/target:Build",
"/restore:True",
"/p:RestoreSources=https://artifactory.intra.infineon.com/artifactory/api/nuget/ngt-fi-package-main-vir/%3Bhttps://packagemanagement.eu.infineon.com:4430/api/v2/%3Bhttps://api.nuget.org/v3/index.json",
"/detailedsummary",
"/consoleloggerparameters:PerformanceSummary;ErrorsOnly;",
"/property:Configuration=Debug;TargetFrameworkVersion=v4.8",
"Fab2ApprovalSystem/Fab2ApprovalSystem.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalMKLink-User Secrets Init",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj",
"init"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalMKLink-User Secrets Set",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj",
"set",
"_UserSecretsId",
"f2da5035-aba9-4676-9f8d-d6689f84663d"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalMKLink-Format",
"command": "dotnet",
"type": "process",
"args": [
"format",
"--report",
".vscode",
"--verbosity",
"detailed",
"--severity",
"warn"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalMKLink-Format-Whitespaces",
"command": "dotnet",
"type": "process",
"args": [
"format",
"whitespace"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalMKLink-build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalMKLink-publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalMKLink-watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalMKLink-Publish AOT",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"-r",
"win-x64",
"-c",
"Release",
"-p:PublishAot=true",
"${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalTests-User Secrets Init",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj",
"init"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalTests-User Secrets Set",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj",
"set",
"_UserSecretsId",
"3942d1fb-d585-40ae-8985-d276d1b94b77"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalTests-Format",
"command": "dotnet",
"type": "process",
"args": [
"format",
"--report",
".vscode",
"--verbosity",
"detailed",
"--severity",
"warn"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalTests-Format-Whitespaces",
"command": "dotnet",
"type": "process",
"args": [
"format",
"whitespace"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalTests-build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalTests-testDebug",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalTests-testRelease",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"-c",
"Release"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalTests-publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalTests-watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "Fab2ApprovalTests-Publish AOT",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"-r",
"win-x64",
"-c",
"Release",
"-p:PublishAot=true",
"${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.API-User Secrets Init",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj",
"init"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.API-User Secrets Set",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj",
"set",
"_UserSecretsId",
"0b98e1f2-95ed-4edd-8149-58cce51ca059"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.API-Format",
"command": "dotnet",
"type": "process",
"args": [
"format",
"--report",
".vscode",
"--verbosity",
"detailed",
"--severity",
"warn"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.API-Format-Whitespaces",
"command": "dotnet",
"type": "process",
"args": [
"format",
"whitespace"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.API-build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.API-testDebug",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.API-testRelease",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"-c",
"Release"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.API-publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.API-watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.API-Publish AOT",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"-r",
"win-x64",
"-c",
"Release",
"-p:PublishAot=true",
"${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.Client-User Secrets Init",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj",
"init"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.Client-Format",
"command": "dotnet",
"type": "process",
"args": [
"format",
"--report",
".vscode",
"--verbosity",
"detailed",
"--severity",
"warn"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.Client-Format-Whitespaces",
"command": "dotnet",
"type": "process",
"args": [
"format",
"whitespace"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.Client-build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.Client-testDebug",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.Client-testRelease",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"-c",
"Release"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.Client-publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.Client-watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "MesaFabApproval.Client-Publish AOT",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"-r",
"win-x64",
"-c",
"Release",
"-p:PublishAot=true",
"${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@ -1,20 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2020
# Visual Studio Version 17
VisualStudioVersion = 17.9.34616.47
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fab2ApprovalSystem", "Fab2ApprovalSystem\Fab2ApprovalSystem.csproj", "{AAE52608-4DD1-4732-92BD-CC8915DEC71E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MesaFabApproval.API", "MesaFabApproval.API\MesaFabApproval.API.csproj", "{852E528D-015A-43B5-999D-F281E3359E5E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MesaFabApproval.Shared", "MesaFabApproval.Shared\MesaFabApproval.Shared.csproj", "{2C16014D-B04E-46AF-AB4C-D2691D44A339}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MesaFabApproval.Client", "MesaFabApproval.Client\MesaFabApproval.Client.csproj", "{34D52F44-A81F-4247-8180-16E204824A07}"
ProjectSection(ProjectDependencies) = postProject
{2C16014D-B04E-46AF-AB4C-D2691D44A339} = {2C16014D-B04E-46AF-AB4C-D2691D44A339}
EndProjectSection
EndProject
Global
GlobalSection(TeamFoundationVersionControl) = preSolution
SccNumberOfProjects = 2
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
SccTeamFoundationServer = http://tfs.intra.infineon.com:8080/tfs/manufacturingit
SccLocalPath0 = .
SccProjectUniqueName1 = Fab2ApprovalSystem\\Fab2ApprovalSystem.csproj
SccProjectName1 = Fab2ApprovalSystem
SccLocalPath1 = Fab2ApprovalSystem
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
@ -24,8 +24,23 @@ Global
{AAE52608-4DD1-4732-92BD-CC8915DEC71E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AAE52608-4DD1-4732-92BD-CC8915DEC71E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AAE52608-4DD1-4732-92BD-CC8915DEC71E}.Release|Any CPU.Build.0 = Release|Any CPU
{852E528D-015A-43B5-999D-F281E3359E5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{852E528D-015A-43B5-999D-F281E3359E5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{852E528D-015A-43B5-999D-F281E3359E5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{852E528D-015A-43B5-999D-F281E3359E5E}.Release|Any CPU.Build.0 = Release|Any CPU
{2C16014D-B04E-46AF-AB4C-D2691D44A339}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2C16014D-B04E-46AF-AB4C-D2691D44A339}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2C16014D-B04E-46AF-AB4C-D2691D44A339}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2C16014D-B04E-46AF-AB4C-D2691D44A339}.Release|Any CPU.Build.0 = Release|Any CPU
{34D52F44-A81F-4247-8180-16E204824A07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34D52F44-A81F-4247-8180-16E204824A07}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34D52F44-A81F-4247-8180-16E204824A07}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34D52F44-A81F-4247-8180-16E204824A07}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A966A184-1FCD-4B6A-978C-5907CC12406B}
EndGlobalSection
EndGlobal

View File

@ -17,7 +17,7 @@ namespace Fab2ApprovalSystem
LoginPath = new PathString("/Account/Login")
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(

View File

@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Http;
namespace Fab2ApprovalSystem
{
namespace Fab2ApprovalSystem {
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
@ -12,20 +11,24 @@ using Fab2ApprovalSystem.Models;
using System.Web.Security;
using Fab2ApprovalSystem.Misc;
using Fab2ApprovalSystem.DMO;
using Microsoft.AspNet.Identity.Owin;
using System.Net.Http;
using Newtonsoft.Json;
using System.Text;
using System.Net;
namespace Fab2ApprovalSystem.Controllers
{
namespace Fab2ApprovalSystem.Controllers {
[Authorize]
public class AccountController : Controller
{
public class AccountController : Controller {
private string _apiBaseUrl;
public AccountController()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()))) {
_apiBaseUrl = Environment.GetEnvironmentVariable("FabApprovalApiBaseUrl") ??
throw new ArgumentNullException("FabApprovalApiBaseUrl environment variable not found");
}
public AccountController(UserManager<ApplicationUser> userManager)
{
public AccountController(UserManager<ApplicationUser> userManager) {
UserManager = userManager;
}
@ -35,9 +38,8 @@ namespace Fab2ApprovalSystem.Controllers
// GET: /Account/Login
[AllowAnonymous]
// try to make the browser refresh the login page every time, to prevent issues with changing usernames and the anti-forgery token validation
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult Login(string returnUrl)
{
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult Login(string returnUrl) {
ViewBag.ReturnUrl = returnUrl;
return View();
}
@ -45,18 +47,32 @@ namespace Fab2ApprovalSystem.Controllers
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
try
{
//if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
//{
// return RedirectToLocal(returnUrl);
//}
UserAccountDMO userDMO = new UserAccountDMO();
public async Task<ActionResult> Login(LoginModel model, string returnUrl) {
try {
bool isLoginValid;
MembershipProvider domainProvider;
HttpClient httpClient = HttpClientFactory.Create();
httpClient.BaseAddress = new Uri(_apiBaseUrl);
AuthAttempt authAttempt = new AuthAttempt() {
LoginID = model.LoginID,
Password = model.Password
};
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "auth/login");
request.Content = new StringContent(JsonConvert.SerializeObject(authAttempt),
Encoding.UTF8,
"application/json");
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
if (!httpResponseMessage.IsSuccessStatusCode)
throw new Exception($"The authentication API failed, because {httpResponseMessage.ReasonPhrase}");
string responseContent = await httpResponseMessage.Content.ReadAsStringAsync();
LoginResult loginResult = JsonConvert.DeserializeObject<LoginResult>(responseContent);
#if(DEBUG)
isLoginValid = true;
@ -65,29 +81,26 @@ namespace Fab2ApprovalSystem.Controllers
#if (!DEBUG)
bool isIFX = false;
//domainProvider = Membership.Providers["NA_ADMembershipProvider"];
//isLoginValid = domainProvider.ValidateUser(model.LoginID, model.Password);
//domainProvider = Membership.Providers["NA_ADMembershipProvider"];
//isLoginValid = domainProvider.ValidateUser(model.LoginID, model.Password);
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY")
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") {
isLoginValid = true;
else
{
isLoginValid = Functions.NA_ADAuthenticate(model.LoginID, model.Password);
if (!isLoginValid)
{
isLoginValid = Functions.IFX_ADAuthenticate(model.LoginID, model.Password);
isIFX = true;
}
} else {
isLoginValid = loginResult.IsAuthenticated;
if (isLoginValid) isIFX = true;
}
#endif
if (isLoginValid)
{
if (isLoginValid) {
UserAccountDMO userDMO = new UserAccountDMO();
LoginModel user = userDMO.GetUser(model.LoginID);
if (user != null)
{
if (user != null) {
Session["JWT"] = loginResult.AuthTokens.JwtToken;
Session["RefreshToken"] = loginResult.AuthTokens.RefreshToken;
Session[GlobalVars.SESSION_USERID] = user.UserID;
Session[GlobalVars.SESSION_USERNAME] = user.FullName;
Session[GlobalVars.IS_ADMIN] = user.IsAdmin;
@ -96,23 +109,16 @@ namespace Fab2ApprovalSystem.Controllers
Session[GlobalVars.CAN_CREATE_PARTS_REQUEST] = user.IsAdmin || PartsRequestController.CanCreatePartsRequest(user.UserID);
FormsAuthentication.SetAuthCookie(user.LoginID, true);
return RedirectToLocal(returnUrl);
}
else
{
} else {
ModelState.AddModelError("", "The user name does not exist in the DB. Please contact the System Admin");
}
}
else
{
} else {
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
catch (Exception ex)
{
Functions.WriteEvent(@User.Identity.Name + " " + ex.InnerException , System.Diagnostics.EventLogEntryType.Error);
} catch (Exception ex) {
Functions.WriteEvent(@User.Identity.Name + " " + ex.InnerException, System.Diagnostics.EventLogEntryType.Error);
EventLogDMO.Add(new WinEventLog() { IssueID = 99999, UserID = @User.Identity.Name, DocumentType = "Login", OperationType = "Error", Comments = "Reject - " + ex.Message });
ModelState.AddModelError("", ex.Message);
}
@ -122,285 +128,167 @@ namespace Fab2ApprovalSystem.Controllers
}
////
//// POST: /Account/Login
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
//{
// if (ModelState.IsValid)
// {
// var user = await UserManager.FindAsync(model.UserName, model.Password);
// if (user != null)
// {
// await SignInAsync(user, model.RememberMe);
// return RedirectToLocal(returnUrl);
// }
// else
// {
// ModelState.AddModelError("", "Invalid username or password.");
// }
// }
[HttpPost]
[AllowAnonymous]
public async Task<HttpResponseMessage> ExternalAuthSetup(AuthAttempt authAttempt) {
try {
bool isLoginValid;
// // If we got this far, something failed, redisplay form
// return View(model);
//}
HttpClient httpClient = HttpClientFactory.Create();
httpClient.BaseAddress = new Uri(_apiBaseUrl);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "auth/refresh");
request.Content = new StringContent(JsonConvert.SerializeObject(authAttempt),
Encoding.UTF8,
"application/json");
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
if (!httpResponseMessage.IsSuccessStatusCode)
throw new Exception($"The authentication API failed, because {httpResponseMessage.ReasonPhrase}");
string responseContent = await httpResponseMessage.Content.ReadAsStringAsync();
LoginResult loginResult = JsonConvert.DeserializeObject<LoginResult>(responseContent);
#if(DEBUG)
isLoginValid = true;
#endif
#if (!DEBUG)
bool isIFX = false;
//domainProvider = Membership.Providers["NA_ADMembershipProvider"];
//isLoginValid = domainProvider.ValidateUser(model.LoginID, model.Password);
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") {
isLoginValid = true;
} else {
isLoginValid = loginResult.IsAuthenticated;
if (isLoginValid) isIFX = true;
}
#endif
if (isLoginValid) {
UserAccountDMO userDMO = new UserAccountDMO();
LoginModel user = userDMO.GetUser(authAttempt.LoginID);
if (user != null) {
Session["JWT"] = loginResult.AuthTokens.JwtToken;
Session["RefreshToken"] = loginResult.AuthTokens.RefreshToken;
Session[GlobalVars.SESSION_USERID] = user.UserID;
Session[GlobalVars.SESSION_USERNAME] = user.FullName;
Session[GlobalVars.IS_ADMIN] = user.IsAdmin;
Session[GlobalVars.IS_MANAGER] = user.IsManager;
Session[GlobalVars.OOO] = user.OOO;
Session[GlobalVars.CAN_CREATE_PARTS_REQUEST] = user.IsAdmin || PartsRequestController.CanCreatePartsRequest(user.UserID);
FormsAuthentication.SetAuthCookie(user.LoginID, true);
return new HttpResponseMessage(HttpStatusCode.OK);
} else {
ModelState.AddModelError("", "The user name does not exist in the DB. Please contact the System Admin");
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
} else {
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
} catch (Exception ex) {
Functions.WriteEvent(@User.Identity.Name + " " + ex.InnerException, System.Diagnostics.EventLogEntryType.Error);
EventLogDMO.Add(new WinEventLog() { IssueID = 99999, UserID = @User.Identity.Name, DocumentType = "Login", OperationType = "Error", Comments = "Reject - " + ex.Message });
ModelState.AddModelError("", ex.Message);
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
//
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
public ActionResult Register() {
return View();
}
//
// POST: /Account/Register
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public async Task<ActionResult> Register(RegisterViewModel model)
//{
// if (ModelState.IsValid)
// {
// var user = new ApplicationUser() { UserName = model.UserName };
// var result = await UserManager.CreateAsync(user, model.Password);
// if (result.Succeeded)
// {
// await SignInAsync(user, isPersistent: false);
// return RedirectToAction("Index", "Home");
// }
// else
// {
// AddErrors(result);
// }
// }
// // If we got this far, something failed, redisplay form
// return View(model);
//}
//
// POST: /Account/Disassociate
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Disassociate(string loginProvider, string providerKey)
{
public async Task<ActionResult> Disassociate(string loginProvider, string providerKey) {
ManageMessageId? message = null;
IdentityResult result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
if (result.Succeeded)
{
if (result.Succeeded) {
message = ManageMessageId.RemoveLoginSuccess;
}
else
{
} else {
message = ManageMessageId.Error;
}
return RedirectToAction("Manage", new { Message = message });
}
//
// GET: /Account/Manage
public ActionResult Manage(ManageMessageId? message)
{
//ViewBag.StatusMessage =
// message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
// : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
// : message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
// : message == ManageMessageId.Error ? "An error has occurred."
// : "";
//ViewBag.HasLocalPassword = HasPassword();
//ViewBag.ReturnUrl = Url.Action("Manage");
#pragma warning disable IDE0060 // Remove unused parameter
public ActionResult Manage(ManageMessageId? message) {
return View();
}
#pragma warning restore IDE0060 // Remove unused parameter
////
//// POST: /Account/Manage
//[HttpPost]
//[ValidateAntiForgeryToken]
//public async Task<ActionResult> Manage(ManageUserViewModel model)
//{
// bool hasPassword = HasPassword();
// ViewBag.HasLocalPassword = hasPassword;
// ViewBag.ReturnUrl = Url.Action("Manage");
// if (hasPassword)
// {
// if (ModelState.IsValid)
// {
// IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
// if (result.Succeeded)
// {
// return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
// }
// else
// {
// AddErrors(result);
// }
// }
// }
// else
// {
// // User does not have a password so remove any validation errors caused by a missing OldPassword field
// ModelState state = ModelState["OldPassword"];
// if (state != null)
// {
// state.Errors.Clear();
// }
// if (ModelState.IsValid)
// {
// IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
// if (result.Succeeded)
// {
// return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
// }
// else
// {
// AddErrors(result);
// }
// }
// }
// // If we got this far, something failed, redisplay form
// return View(model);
//}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
public ActionResult ExternalLogin(string provider, string returnUrl) {
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
////
//// GET: /Account/ExternalLoginCallback
//[AllowAnonymous]
//public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
//{
// var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
// if (loginInfo == null)
// {
// return RedirectToAction("Login");
// }
// // Sign in the user with this external login provider if the user already has a login
// var user = await UserManager.FindAsync(loginInfo.Login);
// if (user != null)
// {
// await SignInAsync(user, isPersistent: false);
// return RedirectToLocal(returnUrl);
// }
// else
// {
// // If the user does not have an account, then prompt the user to create an account
// ViewBag.ReturnUrl = returnUrl;
// ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
// return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { UserName = loginInfo.DefaultUserName });
// }
//}
//
// POST: /Account/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
public ActionResult LinkLogin(string provider) {
// Request a redirect to the external login provider to link a login for the current user
return new ChallengeResult(provider, Url.Action("LinkLoginCallback", "Account"), User.Identity.GetUserId());
}
//
// GET: /Account/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
public async Task<ActionResult> LinkLoginCallback() {
ExternalLoginInfo loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null) {
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
if (result.Succeeded)
{
IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
if (result.Succeeded) {
return RedirectToAction("Manage");
}
return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
}
//
// POST: /Account/ExternalLoginConfirmation
//[HttpPost]
//[AllowAnonymous]
//[ValidateAntiForgeryToken]
//public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
//{
// if (User.Identity.IsAuthenticated)
// {
// return RedirectToAction("Manage");
// }
// if (ModelState.IsValid)
// {
// // Get the information about the user from the external login provider
// var info = await AuthenticationManager.GetExternalLoginInfoAsync();
// if (info == null)
// {
// return View("ExternalLoginFailure");
// }
// var user = new ApplicationUser() { UserName = model.UserName };
// var result = await UserManager.CreateAsync(user);
// if (result.Succeeded)
// {
// result = await UserManager.AddLoginAsync(user.Id, info.Login);
// if (result.Succeeded)
// {
// await SignInAsync(user, isPersistent: false);
// return RedirectToLocal(returnUrl);
// }
// }
// AddErrors(result);
// }
// ViewBag.ReturnUrl = returnUrl;
// return View(model);
//}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
//AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
//AuthenticationManager.SignOut();
public ActionResult LogOff() {
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account");
}
//
// GET: /Account/ExternalLoginFailure
[AllowAnonymous]
public ActionResult ExternalLoginFailure()
{
public ActionResult ExternalLoginFailure() {
return View();
}
[ChildActionOnly]
public ActionResult RemoveAccountList()
{
var linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId());
public ActionResult RemoveAccountList() {
IList<UserLoginInfo> linkedAccounts = UserManager.GetLogins(User.Identity.GetUserId());
ViewBag.ShowRemoveButton = HasPassword() || linkedAccounts.Count > 1;
return (ActionResult)PartialView("_RemoveAccountPartial", linkedAccounts);
}
protected override void Dispose(bool disposing)
{
if (disposing && UserManager != null)
{
protected override void Dispose(bool disposing) {
if (disposing && UserManager != null) {
UserManager.Dispose();
UserManager = null;
}
@ -411,71 +299,52 @@ namespace Fab2ApprovalSystem.Controllers
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
private IAuthenticationManager AuthenticationManager
{
get
{
private IAuthenticationManager AuthenticationManager {
get {
return HttpContext.GetOwinContext().Authentication;
}
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
private async Task SignInAsync(ApplicationUser user, bool isPersistent) {
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
ClaimsIdentity identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
private void AddErrors(IdentityResult result) {
foreach (string error in result.Errors) {
ModelState.AddModelError("", error);
}
}
private bool HasPassword()
{
var user = UserManager.FindById(User.Identity.GetUserId());
if (user != null)
{
private bool HasPassword() {
ApplicationUser user = UserManager.FindById(User.Identity.GetUserId());
if (user != null) {
return user.PasswordHash != null;
}
return false;
}
public enum ManageMessageId
{
public enum ManageMessageId {
ChangePasswordSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
Error
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
private ActionResult RedirectToLocal(string returnUrl) {
if (Url.IsLocalUrl(returnUrl)) {
return Redirect(returnUrl);
}
else
{
//return RedirectToAction("HierarchicalDataTest", "Home");
} else {
return RedirectToAction("MyTasks", "Home");
//return RedirectToAction("Index", "Home", new { tabName = "MyTasks"});
}
}
private class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null)
{
private class ChallengeResult : HttpUnauthorizedResult {
public ChallengeResult(string provider, string redirectUri) : this(provider, redirectUri, null) {
}
public ChallengeResult(string provider, string redirectUri, string userId)
{
public ChallengeResult(string provider, string redirectUri, string userId) {
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
@ -485,11 +354,9 @@ namespace Fab2ApprovalSystem.Controllers
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
if (UserId != null)
{
public override void ExecuteResult(ControllerContext context) {
AuthenticationProperties properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
if (UserId != null) {
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);

View File

@ -102,53 +102,15 @@ namespace Fab2ApprovalSystem.Controllers
// GET: /MRB/Edit/5
public ActionResult Edit(int issueID)
{
MRB mrb = new MRB();
int isITARCompliant = 1;
ViewBag.Status = "Pending";
ViewBag.IsApprover = "false";
ViewBag.IsCloser = "false";
string jwt = Session["JWT"].ToString();
string encodedJwt = System.Net.WebUtility.UrlEncode(jwt);
string refreshToken = Session["RefreshToken"].ToString();
string encodedRefreshToken = System.Net.WebUtility.UrlEncode(refreshToken);
string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ??
"https://localhost:7255";
string mrbUrl = $"{wasmClientUrl}/redirect?jwt={encodedJwt}&refreshToken={encodedRefreshToken}&redirectPath=/mrb/{issueID}";
//ViewBag.IsApproved = "false";
//ViewBag.IsClosed = "false";
PopulateCloseToQDB();
mrb = mrbDMO.GetMRBItem(issueID, out isITARCompliant, (int)Session[GlobalVars.SESSION_USERID]);
ViewBag.UserList = mrbDMO.GetUserList();
if (isITARCompliant == 0) // not ITAR Compliant
{
return View("UnAuthorizedAccess");
}
else
{
if (mrb.ApprovalStatus == (int)GlobalVars.ApprovalOption.Approved)
{
//ViewBag.IsApproved = "true";
ViewBag.Status = "Approved";
}
else if (mrb.ApprovalStatus == (int)GlobalVars.ApprovalOption.Closed)
{
ViewBag.Status = "Closed";
//ViewBag.IsClosed = "true";
}
List<ApproversListViewModel> userList = MiscDMO.GetApproversListByDocument(issueID, mrb.CurrentStep, (int)GlobalVars.DocumentType.MRB);
ApproversListViewModel appUser = userList.Find(delegate (ApproversListViewModel al) { return al.UserID == (int)Session[GlobalVars.SESSION_USERID]; });
if (appUser != null)
{
ViewBag.IsApprover = "true";
}
}
// can edit
ViewBag.Owners = MiscDMO.GetUserList();
ViewBag.Modules = mrbDMO.GetModules();
//ViewBag.Dispositions = mrbDMO.GetDispositions();
ViewBag.RiskAssessments = mrbDMO.GetRiskAssessments();
ViewBag.PartGroups = mrbDMO.GetPartGroups();
ViewBag.DispoTypes = mrbDMO.GetDispositions(issueID).Select(d => new { d.DispositionType });
return View(mrb);
return Redirect(mrbUrl);
}
//
@ -178,39 +140,15 @@ namespace Fab2ApprovalSystem.Controllers
/// <returns></returns>
public ActionResult ReadOnly(int issueID)
{
MRB mrb = new MRB();
int isITARCompliant = 1;
string jwt = Session["JWT"].ToString();
string encodedJwt = System.Net.WebUtility.UrlEncode(jwt);
string refreshToken = Session["RefreshToken"].ToString();
string encodedRefreshToken = System.Net.WebUtility.UrlEncode(refreshToken);
string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ??
"https://localhost:7255";
string mrbUrl = $"{wasmClientUrl}/redirect?jwt={encodedJwt}&refreshToken={encodedRefreshToken}&redirectPath=/mrb/{issueID}";
try
{
if (isITARCompliant == 0) // not ITAR Compliant
{
return View("UnAuthorizedAccess");
}
else
{
mrb = mrbDMO.GetMRBItem(issueID, out isITARCompliant, (int)Session[GlobalVars.SESSION_USERID]);
ViewBag.Owners = MiscDMO.GetUserList();
ViewBag.Modules = mrbDMO.GetModules();
//ViewBag.Dispositions = mrbDMO.GetDispositions();
ViewBag.RiskAssessments = mrbDMO.GetRiskAssessments();
ViewBag.PartGroups = mrbDMO.GetPartGroups();
ViewBag.DispoTypes = mrbDMO.GetDispositions(issueID).Select(d => new { d.DispositionType });
}
return View(mrb);
}
catch (Exception e)
{
string exceptionString = e.Message.ToString().Trim().Length > 500 ? "IssueID=" + issueID.ToString() + " " + e.Message.ToString().Substring(0, 250) : e.Message.ToString();
Functions.WriteEvent(@User.Identity.Name + "\r\n ReadOnly Disposition\r\n" + e.Message.ToString(), System.Diagnostics.EventLogEntryType.Error);
EventLogDMO.Add(new WinEventLog() { IssueID = issueID, UserID = @User.Identity.Name, DocumentType = "Lot Disposition", OperationType = "Error", Comments = exceptionString });
throw new Exception(e.Message);
}
return Redirect(mrbUrl);
}
//

View File

@ -2,25 +2,18 @@ using Fab2ApprovalSystem.DMO;
using Fab2ApprovalSystem.Models;
using Fab2ApprovalSystem.ViewModels;
using Fab2ApprovalSystem.Utilities;
using Kendo.Mvc.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Kendo.Mvc.Extensions;
using Fab2ApprovalSystem.Misc;
using System.Configuration;
using System.Threading.Tasks;
namespace Fab2ApprovalSystem.Controllers
{
namespace Fab2ApprovalSystem.Controllers {
[Authorize]
[SessionExpireFilter]
public class TrainingController : Controller
{
public class TrainingController : Controller {
UserAccountDMO userDMO = new UserAccountDMO();
AdminDMO adminDMO = new AdminDMO();
TrainingDMO trainingDMO = new TrainingDMO();
@ -28,47 +21,39 @@ namespace Fab2ApprovalSystem.Controllers
public EmailUtilities emailer = new EmailUtilities();
// GET: Training
public ActionResult Index()
{
public ActionResult Index() {
return View();
}
//public int Create(int ecnId, List<int> groupIds)
public int Create(int ecnId)
{
public int Create(int ecnId) {
ECN_DMO ecnDMO = new ECN_DMO();
//Delete old training if exists
int oldTrainingId = trainingDMO.GetTrainingId(ecnId);
if (oldTrainingId != null && oldTrainingId != 0)
{
if (oldTrainingId != null && oldTrainingId != 0) {
trainingDMO.DeleteTraining(oldTrainingId);
}
int trainingId = trainingDMO.Create(ecnId);
List<int> TrainingGroups = new List<int>();
TrainingGroups = trainingDMO.GetECNAssignedTrainingGroups(ecnId);
string ECNTitle = ecnDMO.GetECN(ecnId).Title;
List<int> Trainees = new List<int>();
foreach (var group in TrainingGroups)
{
foreach (int group in TrainingGroups) {
Trainees.AddRange(trainingDMO.GetTrainees(group));
}
Trainees = (from a in Trainees select a).Distinct().ToList();
foreach (var trainee in Trainees)
{
foreach (int trainee in Trainees) {
int assignmentId = trainingDMO.CreateAssignment(trainingId, trainee);
NotifyTrainee(trainee, assignmentId, ecnId, ECNTitle);
}
return trainingId;
}
public ActionResult AddUserToTrainingAdHoc(int trainingId, int traineeId, int ecnId)
{
if ((bool)Session[GlobalVars.IS_ADMIN])
{
public ActionResult AddUserToTrainingAdHoc(int trainingId, int traineeId, int ecnId) {
if ((bool)Session[GlobalVars.IS_ADMIN]) {
//Get ECN
ECN ecn = ecnDMO.GetECN(ecnId);
@ -77,178 +62,120 @@ namespace Fab2ApprovalSystem.Controllers
//Get Training
Training training = trainingDMO.GetTraining(trainingId);
if (ecn != null)
{
if (user != null)
{
if (training != null)
{
if (!trainingDMO.IsUserAssigned(traineeId, trainingId))
{
if (training.DeletedDate == null && !ecn.Deleted)
{
if (ecn != null) {
if (user != null) {
if (training != null) {
if (!trainingDMO.IsUserAssigned(traineeId, trainingId)) {
if (training.DeletedDate == null && !ecn.Deleted) {
//Both the ECN and training still exist
if (training.CompletedDate != null)
{
if (training.CompletedDate != null) {
//Training is completed and now we need to re-open it.
trainingDMO.reOpenTraining(trainingId);
int assignmentId = trainingDMO.CreateAssignment(trainingId, traineeId);
NotifyTrainee(traineeId, assignmentId, ecnId, ecn.Title);
return Content("Success");
}
else
{
} else {
//training is still open, just add a user and notify
int assignmentId = trainingDMO.CreateAssignment(trainingId, traineeId);
NotifyTrainee(traineeId, assignmentId, ecnId, ecn.Title);
return Content("Success");
}
}
else
{
} else {
//Ecn or training task have been deleted.
return Content("Training or ECN has been deleted.");
}
}
else
{
} else {
return Content("User already has an open or completed assignment for this training.");
}
}
else
{
} else {
return Content("Invalid training id.");
}
}
else
{
} else {
return Content("invalid userId");
}
}
else
{
} else {
return Content("ECN invalid");
}
}
else
{
} else {
return Content("Not Authorized");
}
}
public ActionResult AddGroupToTrainingAdHoc(int trainingId, int groupId, int ecnId)
{
if ((bool)Session[GlobalVars.IS_ADMIN])
{
public ActionResult AddGroupToTrainingAdHoc(int trainingId, int groupId, int ecnId) {
if ((bool)Session[GlobalVars.IS_ADMIN]) {
ECN ecn = ecnDMO.GetECN(ecnId);
Training training = trainingDMO.GetTraining(trainingId);
TrainingGroup group = trainingDMO.GetTrainingGroupByID(groupId);
List<int> groupMemberIds = trainingDMO.GetTrainees(groupId);
int usersAdded = 0;
if (ecn != null)
{
if (training != null)
{
if (training.DeletedDate == null && !ecn.Deleted)
{
if (training.CompletedDate != null)
{
if (ecn != null) {
if (training != null) {
if (training.DeletedDate == null && !ecn.Deleted) {
if (training.CompletedDate != null) {
//Training is completed and now we need to re-open it.
foreach (int id in groupMemberIds)
{
foreach (int id in groupMemberIds) {
//Check to make sure user doesn't have an active assignment for this training
if (!trainingDMO.IsUserAssigned(id, trainingId))
{
if (!trainingDMO.IsUserAssigned(id, trainingId)) {
usersAdded++;
int assignmentId = trainingDMO.CreateAssignment(trainingId, id);
NotifyTrainee(id, assignmentId, ecnId, ecn.Title);
}
}
if (usersAdded > 0)
{
if (usersAdded > 0) {
trainingDMO.reOpenTraining(trainingId);
}
}
else
{
} else {
//training is still open, just add a users and notify
foreach (int id in groupMemberIds)
{
foreach (int id in groupMemberIds) {
//Check to make sure user doesn't have an active assignment for this training
if (!trainingDMO.IsUserAssigned(id, trainingId))
{
if (!trainingDMO.IsUserAssigned(id, trainingId)) {
usersAdded++;
int assignmentId = trainingDMO.CreateAssignment(trainingId, id);
NotifyTrainee(id, assignmentId, ecnId, ecn.Title);
}
}
}
if(usersAdded > 0)
{
try
{
if (usersAdded > 0) {
try {
trainingDMO.AddTrainingGroupToECN(ecnId, groupId);
}
catch(Exception e)
{
} catch (Exception e) {
return Content(e.ToString());
}
}
return Content("Success. " + usersAdded + " users added.");
}
else
{
} else {
return Content("Training or ECN has been deleted.");
}
}
else
{
} else {
return Content("Invalid training id.");
}
}
else
{
} else {
return Content("ECN invalid");
}
}
else
{
} else {
return Content("Not Authorized");
}
}
public ActionResult DeleteTrainingByECN(int ECNNumber)
{
public ActionResult DeleteTrainingByECN(int ECNNumber) {
int trainingId = trainingDMO.GetTrainingId(ECNNumber);
if (trainingId != null && trainingId != 0)
{
if (trainingId != null && trainingId != 0) {
trainingDMO.DeleteTraining(trainingId);
}
return RedirectToAction("ViewTrainings");
}
public ActionResult DeleteTrainingByID(int trainingId)
{
if (trainingId != null && trainingId != 0)
{
public ActionResult DeleteTrainingByID(int trainingId) {
if (trainingId != null && trainingId != 0) {
trainingDMO.DeleteTraining(trainingId);
}
return RedirectToAction("ViewTrainings");
}
public void NotifyTrainee(int userId, int assignmentId, int ecnId, string title)
{
try
{
public void NotifyTrainee(int userId, int assignmentId, int ecnId, string title) {
try {
string emailSentList = "";
//ECN ecn = ecnDMO.GetECN(ecnNumber);
//List<string> emailIst = ldDMO.GetApproverEmailList(@issueID, currentStep).Distinct().ToList();
string recipient = userDMO.GetUserEmailByID(userId.ToString());
string emailTemplate = "ECNTrainingAssigned.txt";
@ -258,132 +185,98 @@ namespace Fab2ApprovalSystem.Controllers
subject = "ECN# " + ecnId + " - Training Assignment Notice - " + title;
EmailNotification en = new EmailNotification(subject, ConfigurationManager.AppSettings["EmailTemplatesPath"]);
//string emailparams = "";
userEmail = recipient;
string[] emailparams = new string[4];
emailparams[0] = assignmentId.ToString();
emailparams[1] = ecnId.ToString();
emailparams[2] = GlobalVars.hostURL;
EmailNotification en = new EmailNotification(subject, ConfigurationManager.AppSettings["EmailTemplatesPath"]);
userEmail = recipient;
string[] emailparams = new string[4];
emailparams[0] = assignmentId.ToString();
emailparams[1] = ecnId.ToString();
emailparams[2] = GlobalVars.hostURL;
//#if(DEBUG)
//string SenderEmail = "MesaFabApproval@infineon.com";
//userEmail = "jonathan.ouellette@infineon.com";
//#endif
en.SendNotificationEmail(emailTemplate, GlobalVars.SENDER_EMAIL, senderName, userEmail, null, subject, emailparams);
//en.SendNotificationEmail(emailTemplate, SenderEmail, senderName, userEmail, null, subject, emailparams);
}
catch (Exception e)
{
} catch (Exception e) {
string detailedException = "";
try
{
try {
detailedException = e.InnerException.ToString();
}
catch
{
} catch {
detailedException = e.Message;
}
}
}
public ActionResult ViewTrainingPartial(int trainingID, int userID)
{
public ActionResult ViewTrainingPartial(int trainingID, int userID) {
List<TrainingAssignment> TrainingData = trainingDMO.GetTrainingAssignmentsByUser(trainingID, userID);
if (trainingID > 0)
{
if (trainingID > 0) {
ViewBag.ECNNumber = trainingDMO.GetTraining(trainingID).ECN;
}
return PartialView(TrainingData);
}
public ActionResult ViewTrainingDocsPartial(int trainingAssignmentId)
{
public ActionResult ViewTrainingDocsPartial(int trainingAssignmentId) {
ViewBag.trainingAssignmentId = trainingAssignmentId;
//IEnumerable<TrainingDocAck> attachments = ecnDMO.GetECNAttachments(ecnNumber);
IEnumerable<TrainingDocAck> attachments = trainingDMO.GetAssignedDocs(trainingAssignmentId);
return PartialView(attachments);
}
public ActionResult AcknowledgeDocument(int trainingAssignmentID, int trainingDocAckID)
{
public ActionResult AcknowledgeDocument(int trainingAssignmentID, int trainingDocAckID) {
//Check to see if acknowledgement is valid(Security Feature to protect data integrity)
if (trainingDMO.CheckValidDocAck(trainingDocAckID))
{
if (trainingDMO.CheckValidDocAck(trainingDocAckID)) {
trainingDMO.AcknowledgeDocument(trainingDocAckID);
bool isFinishedTrainingAssignment = trainingDMO.CheckTrainingAssignmentStatus(trainingAssignmentID);
if (isFinishedTrainingAssignment)
{
try
{
if (isFinishedTrainingAssignment) {
try {
trainingDMO.UpdateAssignmentStatus(trainingAssignmentID);
bool isFinishedTraining = trainingDMO.CheckTrainingStatus(trainingAssignmentID);
if (isFinishedTraining)
{
if (isFinishedTraining) {
int TrainingID = trainingDMO.GetTrainingIdByAssignment(trainingAssignmentID);
trainingDMO.UpdateTrainingStatus(TrainingID);
}
}
catch (Exception e)
{
} catch (Exception e) {
string exception = e.ToString();
return Content(exception);
}
}
}
return Content("Marked Succesfully.");
}
public ActionResult AcknowledgeReviewNoDocuments(int trainingAssignmentID)
{
try
{
public ActionResult AcknowledgeReviewNoDocuments(int trainingAssignmentID) {
try {
trainingDMO.UpdateAssignmentStatus(trainingAssignmentID);
bool isFinishedTraining = trainingDMO.CheckTrainingStatus(trainingAssignmentID);
if (isFinishedTraining)
{
if (isFinishedTraining) {
int TrainingID = trainingDMO.GetTrainingIdByAssignment(trainingAssignmentID);
trainingDMO.UpdateTrainingStatus(TrainingID);
}
}
catch (Exception e)
{
} catch (Exception e) {
string exception = e.ToString();
return Content(exception, "application/json");
}
return Json(new { test = "Succesfully saved" });
}
//public ActionResult ViewTrainings()
//{
// IEnumerable<Training> trainings = trainingDMO.GetTrainings();
// return View(trainings);
//}
public ActionResult TrainingReports()
{
public ActionResult TrainingReports() {
return View();
}
public ActionResult TrainingReportsView(int? filterType, string filterValue)
{
public ActionResult TrainingReportsView(int? filterType, string filterValue) {
ViewBag.TrainingGroups = adminDMO.GetTrainingGroups();
IEnumerable<Training> trainingList = trainingDMO.GetAllTrainings();
//Group Filter
if (filterType == 1 && filterValue != "")
{
if (filterType == 1 && filterValue != "") {
ViewBag.GroupFilter = filterValue;
List<Training> filteredTraining = new List<Training>();
foreach (var item in trainingList)
{
foreach (Training item in trainingList) {
List<int> assignedTrainingGroups = trainingDMO.GetECNAssignedTrainingGroups(item.ECN);
foreach (int id in assignedTrainingGroups)
{
if (filterValue == id.ToString())
{
foreach (int id in assignedTrainingGroups) {
if (filterValue == id.ToString()) {
filteredTraining.Add(item);
}
}
@ -392,11 +285,9 @@ namespace Fab2ApprovalSystem.Controllers
return PartialView(trainingList);
}
//Status Filter
if (filterType == 2 && filterValue != "")
{
if (filterType == 2 && filterValue != "") {
List<Training> filteredTraining = new List<Training>();
switch (filterValue)
{
switch (filterValue) {
case "1":
//Completed
filteredTraining = (from a in trainingList where a.Status == true && a.Deleted != true select a).ToList();
@ -414,16 +305,12 @@ namespace Fab2ApprovalSystem.Controllers
return PartialView(trainingList);
}
//Default return all.
else
{
else {
return PartialView(trainingList);
}
}
public ActionResult ViewTrainingAssignmentsReportView(int trainingID, string statusFilter, string groupFilter)
{
public ActionResult ViewTrainingAssignmentsReportView(int trainingID, string statusFilter, string groupFilter) {
bool? trainingStatus = trainingDMO.GetTraining(trainingID).Status;
int ECNNumber = trainingDMO.GetTraining(trainingID).ECN;
string ECNTitle = ecnDMO.GetECN(ECNNumber).Title;
@ -438,37 +325,30 @@ namespace Fab2ApprovalSystem.Controllers
//float assignmentCount = trainingAssignments.Count();
float assignmentCount = (from a in trainingAssignments where a.Deleted != true select a).Count();
float totalCompleted = 0;
foreach (var assignment in trainingAssignments)
{
if (assignment.status == true && assignment.Deleted != true)
{
foreach (TrainingAssignment assignment in trainingAssignments) {
if (assignment.status == true && assignment.Deleted != true) {
totalCompleted++;
}
}
percentComplete = (totalCompleted / assignmentCount) * 100;
percentComplete = totalCompleted / assignmentCount * 100;
ViewBag.PercentComplete = percentComplete.ToString("0.00") + "%";
if (groupFilter != "" && groupFilter != null)
{
if (groupFilter != "" && groupFilter != null) {
ViewBag.GroupFilter = groupFilter;
List<TrainingAssignment> groupFilteredTraining = new List<TrainingAssignment>();
List<int> groupMemberIds = trainingDMO.GetTrainees(Convert.ToInt32(groupFilter));
foreach (var assignment in trainingAssignments)
{
if(trainingDMO.isUserTrainingMember(Convert.ToInt32(groupFilter), assignment.UserID))
{
foreach (TrainingAssignment assignment in trainingAssignments) {
if (trainingDMO.isUserTrainingMember(Convert.ToInt32(groupFilter), assignment.UserID)) {
groupFilteredTraining.Add(assignment);
}
}
trainingAssignments = groupFilteredTraining;
}
if (statusFilter != "" && statusFilter != null)
{
if (statusFilter != "" && statusFilter != null) {
List<TrainingAssignment> filteredTraining = new List<TrainingAssignment>();
switch (statusFilter)
{
switch (statusFilter) {
case "1":
//Completed
filteredTraining = (from a in trainingAssignments where a.status == true && a.Deleted != true select a).ToList();
@ -491,8 +371,8 @@ namespace Fab2ApprovalSystem.Controllers
return PartialView(trainingAssignments);
}
public ActionResult ViewTrainingAssignments(int trainingID)
{
public ActionResult ViewTrainingAssignments(int trainingID) {
bool? trainingStatus = trainingDMO.GetTraining(trainingID).Status;
int ECNNumber = trainingDMO.GetTraining(trainingID).ECN;
string ECNTitle = ecnDMO.GetECN(ECNNumber).Title;
@ -503,93 +383,78 @@ namespace Fab2ApprovalSystem.Controllers
ViewBag.AllUsers = userDMO.GetAllActiveUsers();
ViewBag.AllGroups = trainingDMO.GetTrainingGroups();
IEnumerable<TrainingAssignment> trainingAssignments = trainingDMO.GetTrainingAssignments(trainingID);
return View(trainingAssignments);
}
/// <summary>
/// Method to return all the training assignments for a specified user
/// </summary>
/// <param name="userID"></param>
/// <returns></returns>
public ActionResult ViewMyTrainingAssignments()
{
public ActionResult ViewMyTrainingAssignments() {
int userID = (int)Session[GlobalVars.SESSION_USERID];
List<TrainingAssignment> assignments = trainingDMO.GetTrainingAssignmentsByUserID(userID);
List<ECNTrainingAssignments> ViewData = new List<ECNTrainingAssignments>();
foreach (var assignment in assignments)
{
foreach (TrainingAssignment assignment in assignments) {
Training training = trainingDMO.GetTraining(assignment.TrainingID);
if (training != null && !assignment.status)
{
if (training != null && !assignment.status) {
int ecnID = training.ECN;
ViewData.Add(new ECNTrainingAssignments
{
ViewData.Add(new ECNTrainingAssignments {
TrainingAssignmentID = assignment.ID,
ECN_ID = ecnID,
TrainingID = assignment.TrainingID,
DateAssigned = assignment.DateAssigned,
DateCompleted = assignment.DateCompleted,
Status = assignment.status
});
}
}
return View(ViewData);
}
/// <summary>
/// Method to return all assigned documents for a specified training assignment
/// </summary>
/// <param name="assignmentID"></param>
/// <returns></returns>
public ActionResult ViewMyTrainingAssignment(int assignmentID, int ECNNumber)
{
public ActionResult ViewMyTrainingAssignment(int assignmentID, int ECNNumber) {
ViewBag.ECNNumber = ECNNumber;
ViewBag.AssignmentID = assignmentID;
ViewBag.IsCompleted = trainingDMO.GetAssignment(assignmentID).status;
return View(trainingDMO.GetAssignedDocs(assignmentID));
}
public ActionResult ViewTrainings(int? filterType, string filterValue)
{
public ActionResult ViewTrainings(int? filterType, string filterValue) {
IEnumerable<Training> AllTrainings = trainingDMO.GetTrainings();
ViewBag.TrainingGroups = adminDMO.GetTrainingGroups();
ViewBag.AllGroups = trainingDMO.GetTrainingGroups();
//Group Filter
if (filterType == 1 && filterValue != "")
{
if (filterType == 1 && filterValue != "") {
ViewBag.GroupFilter = filterValue;
List<Training> filteredTraining = new List<Training>();
foreach (var item in AllTrainings)
{
foreach (Training item in AllTrainings) {
List<int> assignedTrainingGroups = trainingDMO.GetECNAssignedTrainingGroups(item.ECN);
foreach (int id in assignedTrainingGroups)
{
if (filterValue == id.ToString())
{
foreach (int id in assignedTrainingGroups) {
if (filterValue == id.ToString()) {
filteredTraining.Add(item);
}
}
}
AllTrainings = filteredTraining;
return View(AllTrainings);
}
else
{
} else {
ViewBag.AllGroups = trainingDMO.GetTrainingGroups();
return View(AllTrainings);
}
}
public ActionResult ViewAllTrainings()
{
public ActionResult ViewAllTrainings() {
return View();
}
public ActionResult DeleteAssignment(int assignmentId)
{
public ActionResult DeleteAssignment(int assignmentId) {
trainingDMO.DeleteTrainingAssignment(assignmentId);
trainingDMO.DeleteTrainingDocAck(assignmentId);
@ -597,116 +462,81 @@ namespace Fab2ApprovalSystem.Controllers
//TO-DO Put this in its own method.
bool isFinishedTrainingAssignment = trainingDMO.CheckTrainingAssignmentStatus(assignmentId);
if (isFinishedTrainingAssignment)
{
try
{
if (isFinishedTrainingAssignment) {
try {
trainingDMO.UpdateAssignmentStatus(assignmentId);
bool isFinishedTraining = trainingDMO.CheckTrainingStatus(assignmentId);
if (isFinishedTraining)
{
if (isFinishedTraining) {
int TrainingID = trainingDMO.GetTrainingIdByAssignment(assignmentId);
trainingDMO.UpdateTrainingStatus(TrainingID);
}
}
catch (Exception e)
{
} catch (Exception e) {
string exception = e.ToString();
return Content(exception, "application/json");
}
}
return Json(new { test = "Succesfully saved" });
}
public ActionResult ManuallyExecuteECNTraining(int ecnId, int[] trainingGroupsIn)
{
if ((bool)Session[GlobalVars.IS_ADMIN])
{
public ActionResult ManuallyExecuteECNTraining(int ecnId, int[] trainingGroupsIn) {
if ((bool)Session[GlobalVars.IS_ADMIN]) {
List<int> newTrainingGroupIds = new List<int>(trainingGroupsIn);
//Get ECN
ECN ecn = ecnDMO.GetECN(ecnId);
if (ecn != null)
{
if(ecn.CloseDate != null)
{
if (newTrainingGroupIds.Count > 0)
{
if (ecn != null) {
if (ecn.CloseDate != null) {
if (newTrainingGroupIds.Count > 0) {
//Check each assigned group id and see if it's already saved to the ECN
List<int> assignedTrainingGroups = trainingDMO.GetECNAssignedTrainingGroups(ecnId);
IEnumerable<int> onlyNewTrainingIds = newTrainingGroupIds.Except(assignedTrainingGroups);
try
{
foreach (int trainingId in onlyNewTrainingIds)
{
try {
foreach (int trainingId in onlyNewTrainingIds) {
trainingDMO.AddTrainingGroupToECN(ecnId, trainingId);
}
trainingDMO.SetTrainingFlag(ecnId);
Create(ecnId);
return Content("Success");
}
catch(Exception e)
{
} catch (Exception e) {
return Content("Failed: " + e.Message.ToString());
}
}
else
{
} else {
return Content("There were no training groups to assign to. Please select at least one training groups.");
}
}
else
{
} else {
return Content("Selected ECN hasn't been approved yet.");
}
}
else
{
} else {
return Content("Invalid ECN");
}
}
else
{
} else {
return Content("Not Autthorized");
}
}
public ActionResult CheckECN(int ecnId)
{
public ActionResult CheckECN(int ecnId) {
ECN ecn = ecnDMO.GetECN(ecnId);
if(ecn != null)
{
if(ecn.CloseDate != null)
{
if (ecn != null) {
if (ecn.CloseDate != null) {
List<int> trainingGroupIds = trainingDMO.GetECNAssignedTrainingGroups(ecnId);
List<TrainingGroup> assignedGroups = new List<TrainingGroup>();
foreach (int trainingGroupId in trainingGroupIds)
{
foreach (int trainingGroupId in trainingGroupIds) {
TrainingGroup trainingGroup = trainingDMO.GetTrainingGroupByID(trainingGroupId);
assignedGroups.Add(trainingGroup);
}
return Json(trainingGroupIds.ToList());
}
else
{
} else {
return Content("ECN not yet approved.");
}
}
else
{
} else {
return Content("That ECN wasn't found.");
}
}
public bool RunTrainingReport()
{
public bool RunTrainingReport() {
bool isSuccess = false;
try
{
try {
string emailBody = "<h1>Mesa Approval Open Training Assignments Daily Report</h1> <br />";
emailBody += "<p>The following contains open training assignments in the Mesa Approval system. ";
emailBody += "Please ensure the following users complete their training assignments. ";
@ -715,8 +545,7 @@ namespace Fab2ApprovalSystem.Controllers
//Get all users set up to receive the training report email.
List<TrainingReportUser> trainingReportUsers = adminDMO.GetTrainingReportUsers();
List<string> emailList = new List<string>();
foreach (var user in trainingReportUsers)
{
foreach (TrainingReportUser user in trainingReportUsers) {
string userEmail = userDMO.GetUserByID(user.UserId).Email;
emailList.Add(userEmail);
}
@ -724,8 +553,7 @@ namespace Fab2ApprovalSystem.Controllers
//Get a list of open trainings
List<Training> openTrainings = trainingDMO.GetAllOpenTrainings();
foreach (Training training in openTrainings)
{
foreach (Training training in openTrainings) {
string trainingSection = "";
int trainingSectionUserCount = 0;
string ecnTitle = ecnDMO.GetECN(training.ECN).Title;
@ -734,23 +562,18 @@ namespace Fab2ApprovalSystem.Controllers
trainingSection += "<table>";
trainingSection += "<tr><th>Name</th><th>Date Assigned</th></tr>";
List<TrainingAssignment> openAssignments = trainingDMO.GetOpenAssignmentsByTrainingID(training.TrainingID);
foreach (TrainingAssignment assignment in openAssignments)
{
foreach (TrainingAssignment assignment in openAssignments) {
if (!userDMO.GetUserByID(assignment.UserID).OOO)
{
if (!userDMO.GetUserByID(assignment.UserID).OOO) {
trainingSectionUserCount++;
DateTime? assignmentDate = assignment.DateAssigned;
string DateAssigned = assignmentDate.HasValue ? assignmentDate.Value.ToString("MM/dd/yyyy") : "<not available>";
if (assignmentDate.HasValue && (DateTime.Now.Date - assignmentDate.Value.Date).TotalDays > 15)
{
if (assignmentDate.HasValue && (DateTime.Now.Date - assignmentDate.Value.Date).TotalDays > 15) {
trainingSection += "<tr><td>" + assignment.FullName + "</td><td style=\"color:red;\">" + DateAssigned + "</td>";
}
else
{
} else {
trainingSection += "<tr><td>" + assignment.FullName + "</td><td>" + DateAssigned + "</td>";
}
@ -764,13 +587,10 @@ namespace Fab2ApprovalSystem.Controllers
List<string> ccRecipients = emailList;
emailer.SendNotification("MesaFabApproval@infineon.com", ccRecipients, "Mesa Approval Daily Open Training Report", emailBody, "Daily Open Training Report");
isSuccess = true;
}
catch
{
} catch {
isSuccess = false;
}
return isSuccess;
}
}
}

View File

@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Web;
using Fab2ApprovalSystem.Models;
using Dapper;
using System.Transactions;
@ -13,8 +11,7 @@ using Fab2ApprovalSystem.ViewModels;
using System.Reflection;
using Fab2ApprovalSystem.Misc;
namespace Fab2ApprovalSystem.DMO
{
namespace Fab2ApprovalSystem.DMO {
public class LotDispositionDMO
{
private IDbConnection db = new SqlConnection(GlobalVars.DB_CONNECTION_STRING);

View File

@ -154,6 +154,10 @@ namespace Fab2ApprovalSystem.DMO
FabApprovalTrainingEntities db = new FabApprovalTrainingEntities();
var TrainingData = from a in db.TrainingAssignments where a.TrainingID == TrainingID select a;
RefreshTrainingData(TrainingID, TrainingData);
TrainingData = from a in db.TrainingAssignments where a.TrainingID == TrainingID select a;
return TrainingData.ToList();
}
public List<TrainingAssignment> GetTrainingAssignments(int TrainingID)
@ -161,6 +165,10 @@ namespace Fab2ApprovalSystem.DMO
FabApprovalTrainingEntities db = new FabApprovalTrainingEntities();
var TrainingData = from a in db.TrainingAssignments where a.TrainingID == TrainingID && a.Deleted != true select a;
RefreshTrainingData(TrainingID, TrainingData);
TrainingData = from a in db.TrainingAssignments where a.TrainingID == TrainingID select a;
return TrainingData.ToList();
}
public List<TrainingAssignment> GetTrainingAssignmentsByUser(int TrainingID, int userID)
@ -452,5 +460,20 @@ namespace Fab2ApprovalSystem.DMO
return openAssignments;
}
private void RefreshTrainingData(int TrainingID, IQueryable<TrainingAssignment> TrainingData)
{
bool assignmentIsIncomplete = false;
UserAccountDMO userAccountDMO = new UserAccountDMO();
foreach (TrainingAssignment assignment in TrainingData)
{
LoginModel userModel = userAccountDMO.GetUserByID(assignment.UserID);
if (!userModel.IsActive) UpdateAssignmentStatus(assignment.ID);
if (assignment.Deleted != true && (assignment.DateCompleted is null || assignment.DateCompleted > DateTime.Now))
assignmentIsIncomplete = true;
}
if (!assignmentIsIncomplete)
UpdateTrainingStatus(TrainingID);
}
}
}

View File

@ -20,10 +20,14 @@
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
<SccProjectName>
</SccProjectName>
<SccLocalPath>
</SccLocalPath>
<SccAuxPath>
</SccAuxPath>
<SccProvider>
</SccProvider>
<UseGlobalApplicationHostFile />
<Use64BitIISExpress>true</Use64BitIISExpress>
<TargetFrameworkProfile />
@ -149,6 +153,8 @@
<Compile Include="Models\ApprovalLog.cs" />
<Compile Include="Models\ApprovalLogHistory.cs" />
<Compile Include="Models\ApproveListModel.cs" />
<Compile Include="Models\AuthAttempt.cs" />
<Compile Include="Models\AuthTokens.cs" />
<Compile Include="Models\ChangeControlModel.cs" />
<Compile Include="Models\Common.cs" />
<Compile Include="Models\C_8DAuditedStandard.cs">
@ -178,6 +184,7 @@
<DesignTime>True</DesignTime>
<DependentUpon>FabApproval.edmx</DependentUpon>
</Compile>
<Compile Include="Models\LoginResult.cs" />
<Compile Include="Models\LotTravellerModel.cs" />
<Compile Include="Models\PartsRequestModels.cs" />
<Compile Include="Models\TECNNotificationsUser.cs">

View File

@ -6,10 +6,8 @@ using System.Linq;
using System.Net.Mail;
using System.Web;
namespace Fab2ApprovalSystem.Misc
{
public class EmailNotification
{
namespace Fab2ApprovalSystem.Misc {
public class EmailNotification {
#region Variabls
protected string _subject = null;
protected string _TemplatesPath = null;
@ -18,10 +16,8 @@ namespace Fab2ApprovalSystem.Misc
/// <summary>
/// Email subject
/// </summary>
public string EmailSubject
{
public string EmailSubject {
set { _subject = value; }
}
/// <summary>
@ -29,11 +25,9 @@ namespace Fab2ApprovalSystem.Misc
/// </summary>
/// <param name="FileName">File Name</param>
/// <returns>String: Containing the Entire content of the file</returns>
protected string ReadEmailFile(string FileName)
{
protected string ReadEmailFile(string FileName) {
string retVal = null;
try
{
try {
//setting the file name path
string path = _TemplatesPath + FileName;
FileInfo TheFile = new FileInfo(System.Web.HttpContext.Current.Server.MapPath(path));
@ -46,17 +40,12 @@ namespace Fab2ApprovalSystem.Misc
StreamReader sr = new StreamReader(System.Web.HttpContext.Current.Server.MapPath(@path), System.Text.Encoding.GetEncoding(1256));
retVal = sr.ReadToEnd(); // getting the entire text from the file.
sr.Close();
}
catch (Exception ex)
{
} catch (Exception ex) {
throw new Exception("Error Reading File." + ex.Message);
}
return retVal;
}
/// <summary>
/// this function will send email. it will read the mail setting from the web.config
/// </summary>
@ -66,8 +55,8 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="cc">CC ids</param>
/// <param name="email_title">Email Subject</param>
/// <param name="email_body">Email Body</param>
protected void SendEmail(string SenderEmail, string SenderName, string Recep, string cc, string email_title, string email_body)
{
#pragma warning disable IDE0060 // Remove unused parameter
protected void SendEmail(string SenderEmail, string SenderName, string Recep, string cc, string email_title, string email_body) {
// creating email message
MailMessage msg = new MailMessage();
msg.IsBodyHtml = true;// email body will allow html elements
@ -87,27 +76,18 @@ namespace Fab2ApprovalSystem.Misc
msg.Subject = email_title;
msg.Body = email_body;
//create a Smtp Mail which will automatically get the smtp server details from web.config mailSettings section
SmtpClient SmtpMail = new SmtpClient("mailrelay-internal.infineon.com");
//create a Smtp Mail which will automatically get the smtp server details from web.config mailSettings section
SmtpClient SmtpMail = new SmtpClient("mailrelay-internal.infineon.com");
// sending the message.
try
{
try {
SmtpMail.Send(msg);
}
catch (Exception ex)
{
} catch (Exception ex) {
Console.WriteLine("Exception caught in CreateTestMessage2(): {0}",
ex.ToString());
}
}
#pragma warning restore IDE0060 // Remove unused parameter
/// <summary>
///
@ -119,8 +99,7 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="email_title"></param>
/// <param name="email_body"></param>
/// <param name="attachmentPath"></param>
protected void SendEmailWithAttachment(string SenderEmail, string SenderName, string Recep, string cc, string email_title, string email_body, string attachmentPath)
{
protected void SendEmailWithAttachment(string SenderEmail, string SenderName, string Recep, string cc, string email_title, string email_body, string attachmentPath) {
// creating email message
MailMessage msg = new MailMessage();
msg.IsBodyHtml = true;// email body will allow html elements
@ -140,22 +119,14 @@ namespace Fab2ApprovalSystem.Misc
msg.Body = email_body;
msg.Attachments.Add(new Attachment(attachmentPath));
//create a Smtp Mail which will automatically get the smtp server details from web.config mailSettings section
SmtpClient SmtpMail = new SmtpClient();
#if(!DEBUG)
// sending the message.
SmtpMail.Send(msg);
#endif
}
/// <summary>
///
/// </summary>
@ -165,8 +136,8 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="cc"></param>
/// <param name="email_title"></param>
/// <param name="email_body"></param>
protected void SendEmail(string SenderEmail, string SenderName, List<string> RecepientList, string cc, string email_title, string email_body)
{
#pragma warning disable IDE0060 // Remove unused parameter
protected void SendEmail(string SenderEmail, string SenderName, List<string> RecepientList, string cc, string email_title, string email_body) {
// creating email message
MailMessage msg = new MailMessage();
msg.IsBodyHtml = true;// email body will allow html elements
@ -175,8 +146,7 @@ namespace Fab2ApprovalSystem.Misc
//msg.From = new MailAddress(SenderEmail, SenderName);
msg.From = new MailAddress("MesaFabApproval@infineon.com", "Mesa Fab Approval");
// adding the Recepient Email ID
foreach (string recepient in RecepientList)
{
foreach (string recepient in RecepientList) {
msg.To.Add(recepient);
}
@ -188,26 +158,18 @@ namespace Fab2ApprovalSystem.Misc
msg.Subject = email_title;
msg.Body = email_body;
//create a Smtp Mail which will automatically get the smtp server details from web.config mailSettings section
SmtpClient SmtpMail = new SmtpClient();
//create a Smtp Mail which will automatically get the smtp server details from web.config mailSettings section
SmtpClient SmtpMail = new SmtpClient();
// sending the message.
try
{
try {
SmtpMail.Send(msg);
}
catch (Exception ex)
{
} catch (Exception ex) {
Console.WriteLine("Exception caught in CreateTestMessage2(): {0}",
ex.ToString());
}
}
#pragma warning restore IDE0060 // Remove unused parameter
/// <summary>
///
@ -219,8 +181,7 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="email_title"></param>
/// <param name="email_body"></param>
/// <param name="attachmentPath"></param>
protected void SendEmailWithAttachment(string SenderEmail, string SenderName, List<string> RecepientList, string cc, string email_title, string email_body, string attachmentPath)
{
protected void SendEmailWithAttachment(string SenderEmail, string SenderName, List<string> RecepientList, string cc, string email_title, string email_body, string attachmentPath) {
// creating email message
MailMessage msg = new MailMessage();
msg.IsBodyHtml = true;// email body will allow html elements
@ -229,8 +190,7 @@ namespace Fab2ApprovalSystem.Misc
msg.From = new MailAddress(SenderEmail, SenderName);
// adding the Recepient Email ID
foreach (string recepient in RecepientList)
{
foreach (string recepient in RecepientList) {
if (recepient != null)
msg.To.Add(recepient);
}
@ -244,19 +204,13 @@ namespace Fab2ApprovalSystem.Misc
msg.Body = email_body;
msg.Attachments.Add(new Attachment(attachmentPath));
//create a Smtp Mail which will automatically get the smtp server details from web.config mailSettings section
SmtpClient SmtpMail = new SmtpClient();
// sending the message.
SmtpMail.Send(msg);
}
/// <summary>
///
/// </summary>
@ -267,8 +221,7 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="email_title"></param>
/// <param name="email_body"></param>
/// <param name="attachments"></param>
protected void SendEmailWithAttachments(string SenderEmail, string SenderName, string Recep, string cc, string email_title, string email_body, List<string> attachments)
{
protected void SendEmailWithAttachments(string SenderEmail, string SenderName, string Recep, string cc, string email_title, string email_body, List<string> attachments) {
// creating email message
MailMessage msg = new MailMessage();
msg.IsBodyHtml = true;// email body will allow html elements
@ -286,35 +239,29 @@ namespace Fab2ApprovalSystem.Misc
//setting email subject and body
msg.Subject = email_title;
msg.Body = email_body;
foreach (string attachment in attachments)
{
foreach (string attachment in attachments) {
msg.Attachments.Add(new Attachment(attachment));
}
//create a Smtp Mail which will automatically get the smtp server details from web.config mailSettings section
SmtpClient SmtpMail = new SmtpClient();
#if(!DEBUG)
// sending the message.
SmtpMail.Send(msg);
#endif
}
/// <summary>
///
/// </summary>
/// <param name="SenderEmail"></param>
/// <param name="SenderName"></param>
/// <param name="RecepientList"></param>
/// <param name="cc"></param>
/// <param name="email_title"></param>
/// <param name="email_body"></param>
/// <param name="attachments"></param>
protected void SendEmailWithAttachments(string SenderEmail, string SenderName, List<string> RecepientList, string cc, string email_title, string email_body, List<string> attachments)
{
/// <summary>
///
/// </summary>
/// <param name="SenderEmail"></param>
/// <param name="SenderName"></param>
/// <param name="RecepientList"></param>
/// <param name="cc"></param>
/// <param name="email_title"></param>
/// <param name="email_body"></param>
/// <param name="attachments"></param>
protected void SendEmailWithAttachments(string SenderEmail, string SenderName, List<string> RecepientList, string cc, string email_title, string email_body, List<string> attachments) {
// creating email message
MailMessage msg = new MailMessage();
msg.IsBodyHtml = true;// email body will allow html elements
@ -323,8 +270,7 @@ namespace Fab2ApprovalSystem.Misc
msg.From = new MailAddress(SenderEmail, SenderName);
// adding the Recepient Email ID
foreach (string recepient in RecepientList)
{
foreach (string recepient in RecepientList) {
if (recepient != null)
msg.To.Add(recepient);
}
@ -337,44 +283,32 @@ namespace Fab2ApprovalSystem.Misc
msg.Subject = email_title;
msg.Body = email_body;
foreach (string attachment in attachments)
{
foreach (string attachment in attachments) {
msg.Attachments.Add(new Attachment(attachment));
}
//create a Smtp Mail which will automatically get the smtp server details from web.config mailSettings section
SmtpClient SmtpMail = new SmtpClient();
// sending the message.
SmtpMail.Send(msg);
}
public EmailNotification()
{
public EmailNotification() {
}
/// <summary>
/// The Constructor Function
/// </summary>
/// <param name="EmailHeaderSubject">Email Header Subject</param>
/// <param name="TemplatesPath">Emails Files Templates</param>
public EmailNotification(string EmailHeaderSubject, string TemplatesPath)
{
public EmailNotification(string EmailHeaderSubject, string TemplatesPath) {
_subject = EmailHeaderSubject;
_TemplatesPath = TemplatesPath;
}
/// <summary>
/// This function will send the email notification by reading the email template and substitute the arguments
/// </summary>
@ -386,43 +320,33 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="Subject">EMail Subject</param>
/// <param name="Args">Arguments</param>
/// <returns>String: Return the body of the email to be send</returns>
public string SendNotificationEmail(string EmailTemplateFile, string SenderEmail, string SenderName, string RecepientEmail, string CC, string Subject, params string[] Args)
{
public string SendNotificationEmail(string EmailTemplateFile, string SenderEmail, string SenderName, string RecepientEmail, string CC, string Subject, params string[] Args) {
string retVal = null;
//reading the file
string FileContents = ReadEmailFile(EmailTemplateFile);
string emailBody = FileContents;
//setting formatting the string
retVal = string.Format(emailBody, Args);
try
{
try {
//check if we are in debug mode or not. to send email
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY")
{
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") {
SendEmail(SenderEmail, SenderName, GetTestRecipientsList(), CC, (!string.IsNullOrEmpty(Subject) ? Subject + " for: " + RecepientEmail : _subject), retVal);
}
else
{
} else {
#if(!DEBUG)
SendEmail(SenderEmail, SenderName, RecepientEmail, CC, (!string.IsNullOrEmpty(Subject) ? Subject : _subject), retVal);
#endif
}
}
catch (Exception ex)
{
throw ex;
} catch (Exception ex) {
throw ex;
}
return retVal;
}
/// <summary>
/// This function will send the email notification by reading the email template and substitute the arguments along with the attachments
/// </summary>
@ -435,8 +359,7 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="attachmentPath"></param>
/// <param name="Args"></param>
/// <returns></returns>
public string SendNotificationEmailWithAttachment(string EmailTemplateFile, string SenderEmail, string SenderName, string RecepientEmail, string CC, string Subject, string attachmentPath, params string[] Args)
{
public string SendNotificationEmailWithAttachment(string EmailTemplateFile, string SenderEmail, string SenderName, string RecepientEmail, string CC, string Subject, string attachmentPath, params string[] Args) {
string retVal = null;
//reading the file
@ -447,32 +370,22 @@ namespace Fab2ApprovalSystem.Misc
//setting formatting the string
retVal = string.Format(emailBody, Args);
try
{
try {
//check if we are in debug mode or not. to send email
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY")
{
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") {
SendEmailWithAttachment(SenderEmail, SenderName, GetTestRecipientsList(), CC, (!string.IsNullOrEmpty(Subject) ? " TESTING ONLY -IGNORE : " + Subject + RecepientEmail : _subject), retVal, attachmentPath);
}
else
{
} else {
#if(!DEBUG)
SendEmailWithAttachment(SenderEmail, SenderName, RecepientEmail, CC, (!string.IsNullOrEmpty(Subject) ? Subject : _subject), retVal, attachmentPath);
#endif
}
}
catch (Exception ex)
{
} catch (Exception ex) {
throw ex;
}
return retVal;
}
/// <summary>
///
/// </summary>
@ -485,8 +398,7 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="attachmentPath"></param>
/// <param name="Args"></param>
/// <returns></returns>
public string SendNotificationEmailWithAttachment(string EmailTemplateFile, string SenderEmail, string SenderName, List<string> RecepientEmail, string CC, string Subject, string attachmentPath, params string[] Args)
{
public string SendNotificationEmailWithAttachment(string EmailTemplateFile, string SenderEmail, string SenderName, List<string> RecepientEmail, string CC, string Subject, string attachmentPath, params string[] Args) {
string retVal = null;
//reading the file
@ -497,41 +409,28 @@ namespace Fab2ApprovalSystem.Misc
//setting formatting the string
retVal = string.Format(emailBody, Args);
try
{
try {
//check if we are in debug mode or not. to send email
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY")
{
foreach(string email in RecepientEmail)
{
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") {
foreach (string email in RecepientEmail) {
Subject += email + ";";
}
RecepientEmail.Clear();
SendEmailWithAttachment(SenderEmail, SenderName, GetTestRecipientsList(), CC, (!string.IsNullOrEmpty(Subject) ? " TESTING ONLY -IGNORE : " + Subject : _subject), retVal, attachmentPath);
}
else
{
} else {
#if (!DEBUG)
SendEmailWithAttachment(SenderEmail, SenderName, RecepientEmail, CC, (!string.IsNullOrEmpty(Subject) ? Subject : _subject), retVal, attachmentPath);
#endif
}
}
catch (Exception ex)
{
} catch (Exception ex) {
throw ex;
}
return retVal;
}
/////////============================================================
public string SendNotificationEmailWithAttachments(string EmailTemplateFile, string SenderEmail, string SenderName, string RecepientEmail, string CC, string Subject, List<string> attachments, params string[] Args)
{
public string SendNotificationEmailWithAttachments(string EmailTemplateFile, string SenderEmail, string SenderName, string RecepientEmail, string CC, string Subject, List<string> attachments, params string[] Args) {
string retVal = null;
//reading the file
@ -542,32 +441,22 @@ namespace Fab2ApprovalSystem.Misc
//setting formatting the string
retVal = string.Format(emailBody, Args);
try
{
try {
//check if we are in debug mode or not. to send email
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY")
{
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") {
SendEmailWithAttachments(SenderEmail, SenderName, GetTestRecipientsList(), CC, (!string.IsNullOrEmpty(Subject) ? " TESTING ONLY -IGNORE : " + Subject + RecepientEmail : _subject), retVal, attachments);
}
else
{
} else {
#if(!DEBUG)
SendEmailWithAttachments(SenderEmail, SenderName, RecepientEmail, CC, (!string.IsNullOrEmpty(Subject) ? Subject : _subject), retVal, attachments);
#endif
}
}
catch (Exception ex)
{
} catch (Exception ex) {
throw ex;
}
return retVal;
}
/// <summary>
///
/// </summary>
@ -580,8 +469,7 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="attachmentPath"></param>
/// <param name="Args"></param>
/// <returns></returns>
public string SendNotificationEmailWithAttachments(string EmailTemplateFile, string SenderEmail, string SenderName, List<string> RecepientEmail, string CC, string Subject, List<string> attachments, params string[] Args)
{
public string SendNotificationEmailWithAttachments(string EmailTemplateFile, string SenderEmail, string SenderName, List<string> RecepientEmail, string CC, string Subject, List<string> attachments, params string[] Args) {
string retVal = null;
//reading the file
@ -592,39 +480,27 @@ namespace Fab2ApprovalSystem.Misc
//setting formatting the string
retVal = string.Format(emailBody, Args);
try
{
try {
//check if we are in debug mode or not. to send email
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY")
{
foreach(string email in RecepientEmail)
{
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") {
foreach (string email in RecepientEmail) {
Subject += email + ";";
}
RecepientEmail.Clear();
SendEmailWithAttachments(SenderEmail, SenderName, GetTestRecipientsList(), CC, (!string.IsNullOrEmpty(Subject) ? " TESTING ONLY -IGNORE : " + Subject : _subject), retVal, attachments);
}
else
{
} else {
#if (!DEBUG)
SendEmailWithAttachments(SenderEmail, SenderName, RecepientEmail, CC, (!string.IsNullOrEmpty(Subject) ? Subject : _subject), retVal, attachments);
#endif
}
}
catch (Exception ex)
{
} catch (Exception ex) {
throw ex;
}
return retVal;
}
/// <summary>
///
/// </summary>
@ -636,8 +512,7 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="Subject"></param>
/// <param name="Args"></param>
/// <returns></returns>
public string SendNotificationEmail(string EmailTemplateFile, string SenderEmail, string SenderName, List<string> RecepientEmail, string CC, string Subject, params string[] Args)
{
public string SendNotificationEmail(string EmailTemplateFile, string SenderEmail, string SenderName, List<string> RecepientEmail, string CC, string Subject, params string[] Args) {
string retVal = null;
//reading the file
@ -648,33 +523,24 @@ namespace Fab2ApprovalSystem.Misc
//setting formatting the string
retVal = string.Format(emailBody, Args);
try
{
try {
//check if we are in debug mode or not. to send email
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY")
{
foreach (string email in RecepientEmail)
{
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") {
foreach (string email in RecepientEmail) {
Subject += email + ";";
}
RecepientEmail.Clear();
SendEmail(SenderEmail, SenderName, GetTestRecipientsList(), CC, (!string.IsNullOrEmpty(Subject) ? Subject : _subject), retVal);
}
else
{
} else {
#if (!DEBUG)
SendEmail(SenderEmail, SenderName, RecepientEmail, CC, (!string.IsNullOrEmpty(Subject) ? Subject : _subject), retVal);
#endif
}
}
catch (Exception ex)
{
} catch (Exception ex) {
throw ex;
}
return retVal;
}
/// <summary>
@ -683,10 +549,8 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="subject"></param>
/// <param name="body"></param>
/// <param name="importance"></param>
public void SendNotificationEmailToAdmin(string subject, string body, MailPriority importance)
{
try
{
public void SendNotificationEmailToAdmin(string subject, string body, MailPriority importance) {
try {
System.Configuration.ConfigurationManager.RefreshSection("appSettings");
SmtpClient client = new SmtpClient(ConfigurationManager.AppSettings["SMTP Server"]);
@ -704,30 +568,23 @@ namespace Fab2ApprovalSystem.Misc
msg.Subject = subject;
msg.Body = temp;
msg.Priority = importance;
//#if(!DEBUG)
//#if(!DEBUG)
client.Send(msg);
//#endif
}
catch (Exception ex)
{
//#endif
} catch (Exception ex) {
throw ex;
}
}
public List<string> GetTestRecipientsList()
{
var r = new List<string>();
try
{
public List<string> GetTestRecipientsList() {
List<string> r = new List<string>();
try {
string emails = ConfigurationManager.AppSettings["Test Email Recipients"];
foreach (string s in emails.Split(';', ','))
{
foreach (string s in emails.Split(';', ',')) {
if (!String.IsNullOrWhiteSpace(s))
r.Add(s);
}
}
catch
{
} catch {
r.Add("dhuang2@infineon.com");
}
return r;

View File

@ -10,57 +10,44 @@ using System.Net.Mail;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
namespace Fab2ApprovalSystem.Misc
{
public static class Functions
{
namespace Fab2ApprovalSystem.Misc {
public static class Functions {
/// <summary>
/// Writes to the Application Event Log and sends an email notification if appropriate
/// </summary>
/// <param name="logtext"></param>
/// <param name="eventType"></param>
public static void WriteEvent(string logtext, System.Diagnostics.EventLogEntryType eventType)
{
public static void WriteEvent(string logtext, System.Diagnostics.EventLogEntryType eventType) {
//#if(!DEBUG)
EmailNotification en = new EmailNotification();
EventLog ev = new EventLog("Application");
ev.Source = "Fab Approval System";
try
{
try {
//Write to the Event Log
ev.WriteEntry(logtext, eventType);
////Send an email notification if appropriate
////Don't attempt to send an email if the error is pertaining to an email problem
if (!logtext.Contains("SendEmailNotification()"))
{
if (!logtext.Contains("SendEmailNotification()")) {
//Only send email notifications for Error and Warning level events
if (eventType == System.Diagnostics.EventLogEntryType.Error)
en.SendNotificationEmailToAdmin(ev.Source + " - Error Notification", logtext, MailPriority.High);
//else if (eventType == System.Diagnostics.EventLogEntryType.Warning)
// SendEmailNotification(ErrorRecipient(), ev.Source + " Warning Event Logged", logtext, NORMAL_PRI);
}
}
catch
{
} catch {
//throw;
}
finally
{
} finally {
ev = null;
}
//#endif
}
/// <summary>
/// Returns the FTP Server Name
/// </summary>
/// <returns></returns>
public static string FTPServer()
{
public static string FTPServer() {
ConfigurationManager.RefreshSection("appSettings");
return ConfigurationManager.AppSettings["FTP Server"];
}
@ -69,8 +56,7 @@ namespace Fab2ApprovalSystem.Misc
/// Returns the FTP User Name
/// </summary>
/// <returns></returns>
public static string FTPUser()
{
public static string FTPUser() {
ConfigurationManager.RefreshSection("appSettings");
return ConfigurationManager.AppSettings["FTP User"];
}
@ -79,8 +65,7 @@ namespace Fab2ApprovalSystem.Misc
/// Returns the FTP Password
/// </summary>
/// <returns></returns>
public static string FTPPassword()
{
public static string FTPPassword() {
ConfigurationManager.RefreshSection("appSettings");
return ConfigurationManager.AppSettings["FTP Password"];
}
@ -89,41 +74,32 @@ namespace Fab2ApprovalSystem.Misc
///
/// </summary>
/// <returns></returns>
public static string GetAttachmentFolder()
{
public static string GetAttachmentFolder() {
ConfigurationManager.RefreshSection("appSettings");
return ConfigurationManager.AppSettings["AttachmentFolder"];
}
/// <summary>
///
/// </summary>
/// <param name="oldECNNumber"></param>
/// <param name="newECNNumber"></param>
public static void CopyAttachments(int oldECNNumber, int newECNNumber)
{
public static void CopyAttachments(int oldECNNumber, int newECNNumber) {
// The Name of the Upload component is "files"
string oldFolderPath = Functions.GetAttachmentFolder() + "ECN\\" + oldECNNumber.ToString();
string newFolderPath = Functions.GetAttachmentFolder() + "ECN\\" + newECNNumber.ToString() ;
string newFolderPath = Functions.GetAttachmentFolder() + "ECN\\" + newECNNumber.ToString();
DirectoryInfo newdi = new DirectoryInfo(newFolderPath);
if (!newdi.Exists)
newdi.Create();
FileInfo[] existingFiles = new DirectoryInfo(oldFolderPath ).GetFiles();
foreach (FileInfo file in existingFiles)
{
if (!file.Name.Contains("ECNApprovalLog_" + oldECNNumber.ToString()) && !file.Name.Contains("ECNForm_" + oldECNNumber.ToString()))
FileInfo[] existingFiles = new DirectoryInfo(oldFolderPath).GetFiles();
foreach (FileInfo file in existingFiles) {
if (!file.Name.Contains("ECNApprovalLog_" + oldECNNumber.ToString()) && !file.Name.Contains("ECNForm_" + oldECNNumber.ToString()))
//var fileName = Path.GetFileName(file.FullName);
file.CopyTo(Path.Combine(newFolderPath, file.Name));
}
}
}
/// <summary>
@ -132,13 +108,11 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="userID"></param>
/// <param name="pwd"></param>
/// <returns></returns>
public static bool NA_ADAuthenticate(string userID, string pwd)
{
public static bool NA_ADAuthenticate(string userID, string pwd) {
string naContainer = ConfigurationManager.AppSettings["NAContainer"];
string naDomain = ConfigurationManager.AppSettings["NADomain"];
try
{
try {
PrincipalContext contextUser = new PrincipalContext(ContextType.Domain,
naDomain,
naContainer,
@ -149,12 +123,9 @@ namespace Fab2ApprovalSystem.Misc
return false;
else
return true;
}
catch
{
} catch {
return false;
}
}
/// <summary>
@ -163,13 +134,11 @@ namespace Fab2ApprovalSystem.Misc
/// <param name="userID"></param>
/// <param name="pwd"></param>
/// <returns></returns>
public static bool IFX_ADAuthenticate(string userID, string pwd)
{
public static bool IFX_ADAuthenticate(string userID, string pwd) {
string container = ConfigurationManager.AppSettings["IFXContainer"];
string domain = ConfigurationManager.AppSettings["IFXDomain"];
try
{
try {
PrincipalContext contextUser = new PrincipalContext(ContextType.Domain,
domain,
container,
@ -180,18 +149,12 @@ namespace Fab2ApprovalSystem.Misc
return false;
else
return true;
}
catch
{
} catch {
return false;
}
}
public static string FTPSPNBatch()
{
public static string FTPSPNBatch() {
ConfigurationManager.RefreshSection("appSettings");
return ConfigurationManager.AppSettings["FTPSPNBatchFileName"];
}
@ -200,8 +163,7 @@ namespace Fab2ApprovalSystem.Misc
///
/// </summary>
/// <returns></returns>
public static string FTPSPNBatch_Test()
{
public static string FTPSPNBatch_Test() {
ConfigurationManager.RefreshSection("appSettings");
return ConfigurationManager.AppSettings["FTPSPNBatchFileName_Test"];
}
@ -211,50 +173,44 @@ namespace Fab2ApprovalSystem.Misc
/// </summary>
/// <param name="casection"></param>
/// <returns></returns>
public static string CASectionMapper(GlobalVars.CASection casection)
{
switch (casection)
{
public static string CASectionMapper(GlobalVars.CASection casection) {
switch (casection) {
case GlobalVars.CASection.Main:
return "Main";
case GlobalVars.CASection.D1:
return "D1";
case GlobalVars.CASection.D2:
return "D2";
case GlobalVars.CASection.D3:
return "D3";
case GlobalVars.CASection.D4:
return "D4";
case GlobalVars.CASection.D5:
return "D5";
case GlobalVars.CASection.D6:
return "D6";
case GlobalVars.CASection.D7:
return "D7";
case GlobalVars.CASection.D8:
return "D8";
case GlobalVars.CASection.CF: // CA Findings
return "CF";
}
return "";
}
public static string DocumentTypeMapper(GlobalVars.DocumentType docType)
{
switch (docType)
{
case GlobalVars.DocumentType.Audit:
public static string DocumentTypeMapper(GlobalVars.DocumentType docType) {
switch (docType) {
case GlobalVars.DocumentType.Audit:
return "Audit";
case GlobalVars.DocumentType.ChangeControl:
return "ChangeControl";
@ -280,17 +236,15 @@ namespace Fab2ApprovalSystem.Misc
/// </summary>
/// <param name="caNo"></param>
/// <returns></returns>
public static string ReturnCANoStringFormat(int caNo)
{
public static string ReturnCANoStringFormat(int caNo) {
string caNoString = "";
if(caNo == 0)
if (caNo == 0)
return "";
caNoString = "C" + caNo.ToString().PadLeft(5, '0');
return caNoString;
}
public static string ReturnAuditNoStringFormat(int auditNo)
{
public static string ReturnAuditNoStringFormat(int auditNo) {
string auditNoString = "";
if (auditNo == 0)
return "";
@ -298,12 +252,10 @@ namespace Fab2ApprovalSystem.Misc
return auditNoString;
}
public static string ReturnPartsRequestNoStringFormat(int PRNumber)
{
public static string ReturnPartsRequestNoStringFormat(int PRNumber) {
if (PRNumber == 0)
return "";
return String.Format("PR{0:000000}", PRNumber);
}
}
}

View File

@ -0,0 +1,7 @@
namespace Fab2ApprovalSystem.Models {
public class AuthAttempt {
public string LoginID { get; set; }
public string Password { get; set; } = "";
public AuthTokens AuthTokens { get; set; }
}
}

View File

@ -0,0 +1,6 @@
namespace Fab2ApprovalSystem.Models {
public class AuthTokens {
public string JwtToken { get; set; }
public string RefreshToken { get; set; }
}
}

View File

@ -0,0 +1,6 @@
namespace Fab2ApprovalSystem.Models {
public class LoginResult {
public bool IsAuthenticated { get; set; }
public AuthTokens AuthTokens { get; set; }
}
}

View File

@ -14,6 +14,6 @@ namespace Fab2ApprovalSystem.ViewModels
public string Originator { get; set; }
public DateTime? AssignedDate { get; set; }
public DateTime? DueDate { get; set; }
public string pcrMesaID { get; set; }
public string pcrMesaID { get; set; } = string.Empty;
}
}

View File

@ -227,7 +227,7 @@
)
</div>
<div class="col-sm-6 col-sm-offset-4" style="color:red;">
ECN: Qualtiy and Dept. Specific<br />MRB: Quality, Production, Engineering, OPC<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
ECN: Qualtiy and Dept. Specific<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
</div>
</div>
<div class="form-group">

View File

@ -171,7 +171,7 @@
)
</div>
<div class="col-sm-6 col-sm-offset-4" style="color:red;">
ECN: Qualtiy and Dept. Specific<br />MRB: Quality, Production, Engineering, OPC<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
ECN: Qualtiy and Dept. Specific<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
</div>
</div>
<div class="form-group">

View File

@ -259,7 +259,7 @@
)
</div>
<div class="col-sm-6 col-sm-offset-4" style="color:red;">
ECN: Qualtiy and Dept. Specific<br />MRB: Quality, Production, Engineering, OPC<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
ECN: Qualtiy and Dept. Specific<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
</div>
</div>
<div class="form-group">

View File

@ -83,14 +83,33 @@
@*<li><a href=@Url.Action("Create", "LotDisposition")>Lot Dispostion</a></li>*@
@*<li><a href=@Url.Action("Create", "MRB")>Create MRB</a></li>*@
<li><a href=@Url.Action("Create", "ECN")>Create ECN/TECN</a></li>
@if (!string.IsNullOrWhiteSpace(Session["JWT"].ToString())) {
string jwt = Session["JWT"].ToString();
string encodedJwt = System.Net.WebUtility.UrlEncode(jwt);
string refreshToken = Session["RefreshToken"].ToString();
string encodedRefreshToken = System.Net.WebUtility.UrlEncode(refreshToken);
string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ??
"https://localhost:7255";
string mrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/mrb/new";
<li><a href="@mrbUrl">Create MRB</a></li>
@*string pcrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/pcrb/new";
<li><a href="@pcrbUrl">Create PCRB</a></li>*@
} else {
string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ??
"https://localhost:7255";
string mrbUrl = wasmClientUrl + "/redirect?redirectPath=/mrb/new";
<li><a href="@mrbUrl">Create MRB</a></li>
@*string pcrbUrl = wasmClientUrl + "/redirect?redirectPath=/pcrb/new";
<li><a href="@pcrbUrl">Create PCRB</a></li>*@
}
@*<li><a href=@Url.Action("CreateWorkRequest", "LotTraveler")>Create Special Work Request</a></li>*@
@*<li><a href=@Url.Action("Create", "ChangeControl")>Create PCR</a></li>*@
<li><a href=@Url.Action("Create", "Audit")>Create Audit</a></li>
<li><a href=@Url.Action("Create", "CorrectiveAction")>Create Corrective Action</a></li>
@*@if (Convert.ToBoolean(Session[GlobalVars.CAN_CREATE_PARTS_REQUEST]))
{
<li><a href=@Url.Action("Create", "PartsRequest")>Create New/Repair Spare Parts Request</a></li>
}*@
{
<li><a href=@Url.Action("Create", "PartsRequest")>Create New/Repair Spare Parts Request</a></li>
}*@
@*<li><a href="#">Another Doc</a></li>*@
</ul>
</li>
@ -125,6 +144,16 @@
menu.Add().Text("My Training").Action("ViewMyTrainingAssignments", "Training");
menu.Add().Text("Training Reports").Action("TrainingReports", "Training");
menu.Add().Text("All Documents").Action("AllDocuments", "Home");
string jwt = Session["JWT"].ToString();
string encodedJwt = System.Net.WebUtility.UrlEncode(jwt);
string refreshToken = Session["RefreshToken"].ToString();
string encodedRefreshToken = System.Net.WebUtility.UrlEncode(refreshToken);
string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ??
"https://localhost:7255";
string mrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/mrb/all";
menu.Add().Text("MRB").Url(mrbUrl);
//string pcrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/pcrb/all";
//menu.Add().Text("PCRB").Url(pcrbUrl);
//menu.Add().Text("Special Work Requests").Action("SpecialWorkRequestList", "Home");
//menu.Add().Text("PCRB").Action("ChangeControlList", "Home");
//menu.Add().Text("MRB").Action("MRBList", "Home");

View File

@ -11,18 +11,6 @@
<link rel="stylesheet" href="/Content/kendo/kendo.blueopal.min.css" />
<link rel="stylesheet" href="~/Content/kendogridcustom.css" />
@*<link rel="stylesheet" href="~/Scripts/jqwidgets/styles/jqx.base.css" type="text/css" />
<link rel="stylesheet" href="~/Scripts/jqwidgets/styles/jqx.energyblue.css" type="text/css" />
<link rel="stylesheet" href="~/Scripts/jqwidgets/styles/jqx.arctic.css" type="text/css" />
<script type="text/javascript" src="~/Scripts/jqwidgets/jqxcore.js"></script>
<script type="text/javascript" src="~/Scripts/jqwidgets/jqxdata.js"></script>
<script type="text/javascript" src="~/Scripts/jqwidgets/jqxbuttons.js"></script>
<script type="text/javascript" src="~/Scripts/jqwidgets/jqxscrollbar.js"></script>
<script type="text/javascript" src="~/Scripts/jqwidgets/jqxlistbox.js"></script>
<script type="text/javascript" src="~/Scripts/jqwidgets/jqxpanel.js"></script>
<script type="text/javascript" src="~/Scripts/jqwidgets/jqxtree.js"></script>*@
<style>
.k-grid .k-grid-header .k-header .k-link {
height: auto;

View File

@ -132,6 +132,11 @@
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

View File

@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "8.0.6",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}

View File

@ -0,0 +1,260 @@
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# C# files
[*.cs]
#### Core EditorConfig Options ####
# Indentation and spacing
indent_size = 4
indent_style = space
tab_width = 4
# New line preferences
end_of_line = crlf
insert_final_newline = false
#### .NET Coding Conventions ####
# Organize usings
dotnet_separate_import_directive_groups = true
dotnet_sort_system_directives_first = true
file_header_template = unset
# this. and Me. preferences
dotnet_style_qualification_for_event = true
dotnet_style_qualification_for_field = true
dotnet_style_qualification_for_method = true
dotnet_style_qualification_for_property = true
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true
dotnet_style_predefined_type_for_member_access = true
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:error
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:error
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:error
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members
# Expression-level preferences
dotnet_style_coalesce_expression = false
dotnet_style_collection_initializer = true
dotnet_style_explicit_tuple_names = true:error
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true
dotnet_style_object_initializer = true
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true
dotnet_style_prefer_compound_assignment = true
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
dotnet_style_prefer_inferred_anonymous_type_member_names = false
dotnet_style_prefer_inferred_tuple_names = false
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_style_prefer_simplified_boolean_expressions = true
dotnet_style_prefer_simplified_interpolation = true
# Field preferences
dotnet_style_readonly_field = true:warning
# Parameter preferences
dotnet_code_quality_unused_parameters = all:error
# Suppression preferences
dotnet_remove_unnecessary_suppression_exclusions = none
# New line preferences
dotnet_style_allow_multiple_blank_lines_experimental = false:error
dotnet_style_allow_statement_immediately_after_block_experimental = false:warning
#### C# Coding Conventions ####
# var preferences
csharp_style_var_elsewhere = false:error
csharp_style_var_for_built_in_types = false:error
csharp_style_var_when_type_is_apparent = false:error
# Expression-bodied members
csharp_style_expression_bodied_accessors = false
csharp_style_expression_bodied_constructors = false
csharp_style_expression_bodied_indexers = false
csharp_style_expression_bodied_lambdas = true
csharp_style_expression_bodied_local_functions = false
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
csharp_style_expression_bodied_operators = false
csharp_style_expression_bodied_properties = false
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = false
csharp_style_pattern_matching_over_is_with_cast_check = false
csharp_style_prefer_extended_property_pattern = true
csharp_style_prefer_not_pattern = true
csharp_style_prefer_pattern_matching = true
csharp_style_prefer_switch_expression = false
# Null-checking preferences
csharp_style_conditional_delegate_call = false
# Modifier preferences
csharp_prefer_static_local_function = false
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
csharp_style_prefer_readonly_struct = true:warning
csharp_style_prefer_readonly_struct_member = true
# Code-block preferences
csharp_prefer_braces = when_multiline:error
csharp_prefer_simple_using_statement = false
csharp_style_namespace_declarations = file_scoped:error
csharp_style_prefer_method_group_conversion = true:suggestion
csharp_style_prefer_top_level_statements = true:error
# Expression-level preferences
csharp_prefer_simple_default_expression = true
csharp_style_deconstructed_variable_declaration = false
csharp_style_implicit_object_creation_when_type_is_apparent = false
csharp_style_inlined_variable_declaration = true
csharp_style_prefer_index_operator = false:error
csharp_style_prefer_local_over_anonymous_function = true:error
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_range_operator = false:error
csharp_style_prefer_tuple_swap = true
csharp_style_prefer_utf8_string_literals = true
csharp_style_throw_expression = false
csharp_style_unused_value_assignment_preference = unused_local_variable
csharp_style_unused_value_expression_statement_preference = unused_local_variable
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:error
# New line preferences
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:error
csharp_style_allow_embedded_statements_on_same_line_experimental = true
#### C# Formatting Rules ####
# New line preferences
csharp_new_line_before_catch = false
csharp_new_line_before_else = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = none
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
# Question
dotnet_diagnostic.ASP0025.severity = none # Use AddAuthorizationBuilder to register authorization services and construct policies [MesaFabApproval.API]",
dotnet_diagnostic.CA1510.severity = none # Use 'ArgumentNullException.ThrowIfNull' instead of explicitly throwing a new exception instance [MesaFabApproval.API]",
dotnet_diagnostic.CA1806.severity = none # 'MRBNumberIsValid' calls 'Append' but does not use the value the method returns. Linq methods are known to not have side effects. Use the result in a conditional statement, assign the result to a variable, or pass it as an argument to another method. [MesaFabApproval.API]",
dotnet_diagnostic.CA1822.severity = none # Member 'GenerateRefreshToken' does not access instance data and can be marked as static [MesaFabApproval.API]",
dotnet_diagnostic.CA1825.severity = none # Avoid unnecessary zero-length array allocations. Use Array.Empty<string>() instead. [MesaFabApproval.API]",
dotnet_diagnostic.CA1827.severity = none # Count() is used where Any() could be used instead to improve performance [MesaFabApproval.API]",
dotnet_diagnostic.CA1829.severity = none # Use the \"Count\" property instead of Enumerable.Count() [MesaFabApproval.API]",
dotnet_diagnostic.CA1861.severity = none # Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array [MesaFabApproval.API]",
dotnet_diagnostic.CA2208.severity = none # Method UpdateMRBAction passes 'MRB action cannot be null' as the paramName argument to a ArgumentNullException constructor. Replace this argument with one of the method's parameter names. Note that the provided parameter name should have the exact casing as declared on the method. [MesaFabApproval.API]",
dotnet_diagnostic.CA2254.severity = none # The type or namespace name 'AspNetCore' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?) [MesaFabApproval.Shared]",
dotnet_diagnostic.CS0234.severity = none # The type or namespace name 'Extensions' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?) [MesaFabApproval.Shared]",
dotnet_diagnostic.CS0246.severity = none # The type or namespace name 'ILogger<>' could not be found (are you missing a using directive or an assembly reference?) [MesaFabApproval.Shared]",
dotnet_diagnostic.CS1998.severity = none # This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. [MesaFabApproval.API]",
dotnet_diagnostic.CS8019.severity = none # Unnecessary using directive
dotnet_diagnostic.CS8600.severity = none # Converting null literal or possible null value to non-nullable type. [MesaFabApproval.API]",
dotnet_diagnostic.CS8625.severity = none # Cannot convert null literal to non-nullable reference type. [MesaFabApproval.API]",
dotnet_diagnostic.CS8765.severity = none # Nullability of type of parameter 'obj' doesn't match overridden member (possibly because of nullability attributes). [MesaFabApproval.Shared]",
dotnet_diagnostic.IDE0001.severity = none # Name can be simplified
dotnet_diagnostic.IDE0017.severity = none # Object initialization can be simplified [MesaFabApproval.API]",
dotnet_diagnostic.IDE0022.severity = none # Use expression body for method [MesaFabApproval.Shared]",
dotnet_diagnostic.IDE0028.severity = none # Collection initialization can be simplified [MesaFabApproval.API]",
dotnet_diagnostic.IDE0044.severity = none # Make field readonly [MesaFabApproval.API]",
dotnet_diagnostic.IDE0046.severity = none # "'if' statement can be simplified [MesaFabApproval.API]",
dotnet_diagnostic.IDE0052.severity = none # Private member 'CustomerController._cache' can be removed as the value assigned to it is never read [MesaFabApproval.API]",
dotnet_diagnostic.IDE0074.severity = none # Use compound assignment [MesaFabApproval.API]",
dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [MesaFabApproval.API]",
dotnet_diagnostic.IDE0305.severity = none # Collection initialization can be simplified [MesaFabApproval.API]",
dotnet_style_prefer_conditional_expression_over_return = false

View File

@ -0,0 +1 @@
[]

30
MesaFabApproval.API/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,30 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net8.0/MesaFabApproval.API.dll",
"args": [],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
},
{
"type": "node",
"request": "launch",
"name": "node Launch Current Opened File",
"program": "${file}"
}
]
}

6
MesaFabApproval.API/.vscode/mklink.md vendored Normal file
View File

@ -0,0 +1,6 @@
# mklink
```bash 1731705389065 = 638673021890650000 = Fri Nov 15 2024 14:16:28 GMT-0700 (Mountain Standard Time)
mklink /J "L:\DevOps\Mesa_FI\MesaFabApproval\MesaFabApproval.API\.vscode\.UserSecrets" "C:\Users\phares\AppData\Roaming\Microsoft\UserSecrets\0b98e1f2-95ed-4edd-8149-58cce51ca059"
dotnet run --urls=https://localhost:7114/ -C Release
```

View File

@ -0,0 +1,424 @@
{
"[markdown]": {
"editor.wordWrap": "off"
},
"files.exclude": {
"**/.git": false,
"**/node_modules": true
},
"files.watcherExclude": {
"**/node_modules": true
},
"cSpell.words": [
"abutton",
"accessibilities",
"accodingly",
"acknowledgmentby",
"Acks",
"actionsheet",
"Additonal",
"Addtional",
"againm",
"agendaview",
"Antlr",
"Appover",
"Appprrovers",
"Approvalog",
"Aprovers",
"Aproverslist",
"asax",
"aspnetmvc",
"Assignedto",
"Atachments",
"Attachemnt",
"Attachemnts",
"Attchment",
"auditee",
"Auditee",
"Auditees",
"automaically",
"Autthorized",
"beacuase",
"beforeunload",
"Belguim",
"bfound",
"bgcolor",
"Bies",
"binded",
"blackbackground",
"blackhover",
"blackpressed",
"blueenergy",
"blueopal",
"buttongroup",
"BYMRB",
"bytesgot",
"CAID",
"casection",
"CAXXXX",
"CCPCR",
"CCPCRB",
"cellspacing",
"Cheeso's",
"chkbx",
"Clib",
"colorpicker",
"columnmenu",
"columnsreorder",
"columnsresize",
"comming",
"Containmen",
"Copmments",
"correctiv",
"Correctivet",
"Creat",
"currentd",
"Cyle",
"dadada",
"darkbluehover",
"darkbluepressed",
"darkred",
"datafields",
"datasource",
"dataviz",
"datepicker",
"datetimepicker",
"dayview",
"Deletet",
"Delgation",
"DENITED",
"Deparmtent",
"departmentids",
"Descirption",
"devprog",
"dfeffc",
"Disp",
"Dispo",
"Dispoitio",
"Dispos",
"Dispositon",
"Dispostion",
"Dispostions",
"dispotypevalidation",
"Docbase",
"Documentum",
"Documetum",
"dont",
"downlaoded",
"draganddrop",
"dragcancel",
"dropdownlist",
"Eamils",
"ECNPCRB",
"Ecns",
"edmx",
"EECN",
"emai",
"emailparams",
"Emergrncy",
"energyblue",
"Eran",
"Esql",
"ETECN",
"EXCELOPENXML",
"existinglocation",
"Expando",
"extrafield",
"fadc",
"fbec",
"fcfdfd",
"fdff",
"fece",
"feeebd",
"ffdc",
"ffdd",
"fieldset",
"FILIPE",
"filtermenu",
"Fldr",
"flintstone",
"FLOWLOCS",
"FMEA",
"ftplib",
"FTPSPN",
"GETDATE",
"gitea",
"globaldocudms",
"glyphicons",
"groupable",
"Guids",
"halflings",
"Hexsize",
"highcontrast",
"Hmac",
"holdsteps",
"hostspecific",
"icenium",
"IECN",
"imagebrowser",
"IMRB",
"Infineon",
"Insertd",
"inverseicons",
"IPCRB",
"ISADMIN",
"islast",
"ISNULL",
"ITAR",
"jquery",
"jqueryval",
"jqwidgets",
"jqxbuttongroup",
"jqxbuttons",
"jqxcalendar",
"jqxchart",
"jqxcheckbox",
"jqxcolorpicker",
"jqxcombobox",
"jqxcore",
"jqxdata",
"jqxdatatable",
"jqxdatetimeinput",
"jqxdocking",
"jqxdockpanel",
"jqxdragdrop",
"jqxdropdownbutton",
"jqxdropdownlist",
"jqxexpander",
"jqxgauge",
"jqxgrid",
"jqxinput",
"jqxknockout",
"jqxlistbox",
"jqxlistmenu",
"jqxmaskedinput",
"jqxmenu",
"jqxnavigationbar",
"jqxnumberinput",
"jqxpanel",
"jqxpasswordinput",
"jqxprogressbar",
"jqxradiobutton",
"jqxrangeselector",
"jqxrating",
"jqxresponse",
"jqxscrollbar",
"jqxscrollview",
"jqxslider",
"jqxsplitter",
"jqxswitchbutton",
"jqxtabs",
"jqxtooltip",
"jqxtouch",
"jqxtree",
"jqxtreegrid",
"jqxtreemap",
"jqxvalidator",
"jqxwindow",
"kendogridcustom",
"kendoui",
"labelelement",
"labelledby",
"Leanred",
"lightgray",
"linkbutton",
"Linq",
"Listdiv",
"listview",
"Lnks",
"localfilename",
"loclist",
"logis",
"logtext",
"loopmis",
"lotdispo",
"LOTDISPSITION",
"Lotfile",
"lotlist",
"lotstatusoption",
"LTRIM",
"MADUREIRA",
"mailrelay",
"MDTM",
"meego",
"meetingid",
"menubutton",
"mesafi",
"metroblack",
"metrodark",
"miliseconds",
"modalview",
"modernizr",
"Modernizr",
"monthview",
"MRBIs",
"Mrbs",
"msecs",
"multipleextended",
"Navigatable",
"nbsp",
"newbase",
"newchange",
"newdi",
"newfilename",
"newsource",
"Newtonsoft",
"notications",
"Notifcation",
"Notifyf",
"NTLM",
"Nullcc",
"numerictextbox",
"objdata",
"OCAP",
"occured",
"odata",
"oldfilename",
"OLHOLD",
"onclick",
"onmousemove",
"OPDESC",
"OPENQUERY",
"Oper",
"operationslist",
"Orginator",
"Originatorname",
"Ouellette",
"Owin",
"pageable",
"Pageable",
"panelbar",
"parentid",
"parminput",
"parms",
"Parms",
"particula",
"pasv",
"PASV",
"PATINDEX",
"PCRB",
"PCRBID",
"pcrvalues",
"pdbonly",
"Preventitive",
"preventivet",
"Prevetative",
"proces",
"Processedl",
"procs",
"productfamilies",
"progess",
"progressbar",
"qrcode",
"Quanityt",
"rangebar",
"Recep",
"Recepient",
"recieved",
"recordlock",
"remotefilename",
"reorderable",
"reportform",
"reportslist",
"reportslistdiv",
"Reqquired",
"Reqs",
"Requiest",
"Responsibles",
"RETR",
"Revisioing",
"Revisioned",
"Revison",
"rgba",
"rkotian",
"RNFR",
"RNTO",
"Roless",
"roundbg",
"RTRIM",
"SAMDB",
"scroller",
"scrollview",
"seleced",
"selectionlog",
"Selectpart",
"sess",
"Sfisharepoint",
"shinyblack",
"showpassword",
"SIGNON",
"simpleparser",
"slddrw",
"sldprt",
"sortasc",
"sortascbutton",
"sortdesc",
"sortdescbutton",
"sortremove",
"sparkline",
"splitview",
"SPNMRB",
"SPNPDB",
"SSRS",
"Sssign",
"Staus",
"stylesheet",
"Submited",
"subrole",
"subroles",
"Succefully",
"Succesfully",
"sucessfully",
"SURP",
"Swashbuckle",
"SWRN",
"tabindex",
"tabstrip",
"Tahoma",
"taskcompleted",
"Tasklist",
"Taveler",
"TECN",
"TECNs",
"TEMIRWAP",
"tempecd",
"tempimplement",
"templabel",
"tempvalue",
"TEMSA",
"timepicker",
"Tobe",
"Toplevel",
"Totrav",
"trainingby",
"Traininglist",
"traininglistdiv",
"transanction",
"Trav",
"Traveller",
"Traverler",
"TRAVLELER",
"Travler",
"TREEVIEW",
"trigerred",
"ttinclude",
"Uhandled",
"Updat",
"Uplaod",
"Upto",
"userevents",
"userids",
"userlist",
"Validatable",
"valueelement",
"Variabls",
"Verdana",
"vgrid",
"viewmodel",
"vsdoc",
"whethere",
"windowsphone",
"Winsock",
"worlflow"
]
}

135
MesaFabApproval.API/.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,135 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "User Secrets Init",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/MesaFabApproval.API.csproj",
"init"
],
"problemMatcher": "$msCompile"
},
{
"label": "User Secrets Set",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/MesaFabApproval.API.csproj",
"set",
"_UserSecretsId",
"0b98e1f2-95ed-4edd-8149-58cce51ca059"
],
"problemMatcher": "$msCompile"
},
{
"label": "Format",
"command": "dotnet",
"type": "process",
"args": [
"format",
"--report",
".vscode",
"--verbosity",
"detailed",
"--severity",
"warn"
],
"problemMatcher": "$msCompile"
},
{
"label": "Format-Whitespaces",
"command": "dotnet",
"type": "process",
"args": [
"format",
"whitespace"
],
"problemMatcher": "$msCompile"
},
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/MesaFabApproval.API.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "testDebug",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/MesaFabApproval.API.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "testRelease",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/MesaFabApproval.API.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"-c",
"Release"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/MesaFabApproval.API.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/MesaFabApproval.API.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "Publish AOT",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"-r",
"win-x64",
"-c",
"Release",
"-p:PublishAot=true",
"${workspaceFolder}/MesaFabApproval.API.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@ -0,0 +1,20 @@
using System.Net.Mail;
namespace MesaFabApproval.API.Clients;
public interface ISmtpClientWrapper {
void Send(MailMessage message);
}
public class SmtpClientWrapper : ISmtpClientWrapper {
private readonly SmtpClient _client;
public SmtpClientWrapper(SmtpClient client) {
_client = client ??
throw new ArgumentNullException("SmtpClient not injected");
}
public void Send(MailMessage message) {
_client.Send(message);
}
}

View File

@ -0,0 +1,401 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class ApprovalController : ControllerBase {
private readonly ILogger<ApprovalController> _logger;
private readonly IApprovalService _approvalService;
private readonly IMonInWorkerClient _monInClient;
public ApprovalController(ILogger<ApprovalController> logger, IApprovalService approvalService,
IMonInWorkerClient monInClient) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_approvalService = approvalService ?? throw new ArgumentNullException("IApprovalService not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
}
[HttpPost]
[Route("approval")]
public async Task<IActionResult> CreateApproval(Approval approval) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to generate a new approval");
if (approval is null) throw new ArgumentNullException("Approval cannot be null");
await _approvalService.CreateApproval(approval);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot create new approval, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "CreateApproval";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("approval/issue")]
public async Task<IActionResult> GetApprovalsForIssueId(int issueId, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get approvals for issue {issueId}");
if (issueId <= 0) throw new ArgumentException($"{issueId} is not a valid issue ID");
IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(issueId, bypassCache);
return Ok(approvals);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get approvals, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetApprovalsForIssueId";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("approval/user")]
public async Task<IActionResult> GetApprovalsForUserId(int userId, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get approvals for user ID {userId}");
if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID");
IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForUserId(userId, bypassCache);
return Ok(approvals);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get approvals, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetApprovalsForUserId";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("approval/members")]
public async Task<IActionResult> GetApprovalGroupMembers(int subRoleId) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get approval group members for group {subRoleId}");
if (subRoleId <= 0) throw new ArgumentException($"{subRoleId} is not a valid sub role ID");
IEnumerable<User> members = await _approvalService.GetApprovalGroupMembers(subRoleId);
return Ok(members);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get approval group members, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetApprovalsGroupMembers";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPut]
[Route("approval")]
public async Task<IActionResult> UpdateApproval(Approval approval) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to update approval");
if (approval is null) throw new ArgumentNullException($"approval cannot be null");
await _approvalService.UpdateApproval(approval);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot update approval, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UpdateApproval";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPut]
[Route("approval/approve")]
public async Task<IActionResult> Approve(Approval approval) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"attempting to submit approval");
if (approval is null) throw new ArgumentNullException($"approval cannot be null");
await _approvalService.Approve(approval);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot approve, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "Approve";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPut]
[Route("approval/deny")]
public async Task<IActionResult> Deny(Approval approval) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"attempting to deny approval");
if (approval is null) throw new ArgumentNullException($"approval cannot be null");
await _approvalService.Deny(approval);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Approval denial failed, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "Deny";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("approval/roleId")]
public async Task<IActionResult> GetRoleIdForRoleName(string roleName) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get role ID by role name");
if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("role name cannot be null or empty");
int roleId = await _approvalService.GetRoleIdForRoleName(roleName);
return Ok(roleId);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get role ID, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetRoleIdForRoleName";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("approval/subRoles")]
public async Task<IActionResult> GetSubRolesForSubRoleName(string subRoleName, int roleId) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get sub roles by sub role name");
if (string.IsNullOrWhiteSpace(subRoleName)) throw new ArgumentException("sub role name cannot be null or empty");
if (roleId <= 0) throw new ArgumentException($"{roleId} is not a valid role ID");
IEnumerable<SubRole> subRoles = await _approvalService.GetSubRolesForSubRoleName(subRoleName, roleId);
return Ok(subRoles);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get role ID, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetSubRoleIdForSubRoleName";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,178 @@
using System.Security.Authentication;
using System.Security.Principal;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using MesaFabApprovalAPI.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[AllowAnonymous]
public class AuthenticationController : ControllerBase {
private readonly ILogger<AuthenticationController> _logger;
private readonly IMonInWorkerClient _monInClient;
private readonly IAuthenticationService _authenticationService;
public AuthenticationController(ILogger<AuthenticationController> logger,
IMonInWorkerClient monInClient,
IAuthenticationService authenticationService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
_authenticationService = authenticationService ??
throw new ArgumentNullException("IAuthenticationService not injected");
}
[HttpPost]
[Route("auth/login")]
public async Task<IActionResult> Login(AuthAttempt login) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to perform authentication");
if (login is null) throw new ArgumentNullException("Login cannot be null");
LoginResult loginResult = await _authenticationService.AuthenticateUser(login);
if (loginResult.IsAuthenticated)
return Ok(loginResult);
return Unauthorized();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot authenticate user, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "Login";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("auth/login/localWindows")]
public async Task<IActionResult> LoginLocalWindows(WindowsIdentity identity) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to perform local Windows authentication");
if (identity is null) throw new ArgumentNullException("identity cannot be null");
LoginResult loginResult = await _authenticationService.AttemptLocalUserAuth(identity);
if (loginResult.IsAuthenticated)
return Ok(loginResult);
return Unauthorized();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot authenticate local Windows user, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "LoginLocalWindows";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("auth/refresh")]
public async Task<IActionResult> Refresh(AuthAttempt authAttempt) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to refresh auth tokens");
if (authAttempt is null) throw new ArgumentNullException("AuthAttempt cannot be null");
if (authAttempt.AuthTokens is null) throw new ArgumentNullException("AuthTokens cannot be null");
LoginResult loginResult = await _authenticationService.RefreshAuthTokens(authAttempt);
return Ok(loginResult);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (AuthenticationException ex) {
_logger.LogInformation($"Unable to refresh tokens, because {ex.Message}");
return Unauthorized();
} catch (Exception ex) {
isArgumentError = true;
errorMessage = $"Cannot authenticate user, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "RefreshTokens";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpOptions]
[Route("auth/refresh")]
public IActionResult RefreshOptions() {
try {
_logger.LogInformation("Auth refresh options");
return Ok();
} catch (Exception ex) {
_logger.LogError($"Error in auth refresh options. Exception: {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,56 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class CAController : ControllerBase {
private readonly ILogger<ApprovalController> _logger;
private readonly ICAService _caService;
private readonly IMonInWorkerClient _monInClient;
public CAController(ILogger<ApprovalController> logger, ICAService caService,
IMonInWorkerClient monInClient) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_caService = caService ?? throw new ArgumentNullException("ICAService not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
}
[HttpGet]
[Route("ca/isValidCANumber")]
public async Task<IActionResult> IsValidCANumber(int number) {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid CA#");
if (number <= 0) return Ok(false);
bool isValid = await _caService.IsValidCANumber(number);
return Ok(isValid);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot determine if {number} is a valid CA#, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "IsValidCANumber";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,57 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class CustomerController : ControllerBase {
private readonly ILogger<CustomerController> _logger;
private readonly IMonInWorkerClient _monInClient;
private readonly IMemoryCache _cache;
private readonly ICustomerService _customerService;
public CustomerController(ILogger<CustomerController> logger, IMonInWorkerClient monInClient, IMemoryCache cache, ICustomerService customerService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_customerService = customerService ?? throw new ArgumentNullException("ICustomerService");
}
[HttpGet]
[Route("customer/all")]
public async Task<IActionResult> GetAllCustomerNames() {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get all customer names");
IEnumerable<string> customerNames = await _customerService.GetCustomerNames();
return Ok(customerNames);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get user by LoginID, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetAllCustomerNames";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,56 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class ECNController : ControllerBase {
private readonly ILogger<ApprovalController> _logger;
private readonly IECNService _ecnService;
private readonly IMonInWorkerClient _monInClient;
public ECNController(ILogger<ApprovalController> logger, IECNService ecnService,
IMonInWorkerClient monInClient) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_ecnService = ecnService ?? throw new ArgumentNullException("IECNService not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
}
[HttpGet]
[Route("ecn/isValidEcnNumber")]
public async Task<IActionResult> IsValidEcnNumber(int number) {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid ECN#");
if (number <= 0) return Ok(false);
bool isValid = await _ecnService.IsValidECNNumber(number);
return Ok(isValid);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot determine if {number} is a valid ECN#, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "IsValidEcnNumber";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,975 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Models;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
namespace MesaFabApproval.API.Controllers;
[ApiController]
[Authorize]
public class MRBController : ControllerBase {
private readonly ILogger<MRBController> _logger;
private readonly IMRBService _mrbService;
private readonly IMonInWorkerClient _monInClient;
private readonly string _mrbAttachmentPath;
public MRBController(ILogger<MRBController> logger,
IMRBService mrbService,
IMonInWorkerClient monInClient,
AppSettings appSettings) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_mrbService = mrbService ?? throw new ArgumentNullException("IMRBService not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
_mrbAttachmentPath = appSettings.MrbAttachmentPath;
}
[HttpPost]
[Route("mrb/new")]
public async Task<IActionResult> CreateNewMRB(MRB mrb) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to generate a new MRB");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
await _mrbService.CreateNewMRB(mrb);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot create new MRB, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "CreateNewMRB";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpDelete]
[Route("mrb/delete")]
public async Task<IActionResult> DeleteMRB(int mrbNumber) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to delete MRB# {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
await _mrbService.DeleteMRB(mrbNumber);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot delete MRB {mrbNumber}, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "DeleteMRB";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/all")]
public async Task<IActionResult> GetAllMRBs(bool bypassCache) {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get all MRBs");
IEnumerable<MRB> allMrbs = await _mrbService.GetAllMRBs(bypassCache);
return Ok(allMrbs);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get all MRBs, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetAllMRBs";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/numberIsValid")]
public async Task<IActionResult> NumberIsValid(int number) {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid MRB#");
bool isValid = await _mrbService.MRBNumberIsValid(number);
return Ok(isValid);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to determine if {number} is a valid MRB#, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "IsValidMRBNumber";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/getById")]
public async Task<IActionResult> GetMRBById(int id, bool bypassCache = false) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get an MRB by Id");
if (id <= 0) throw new ArgumentException("Invalid MRB number");
MRB mrb = await _mrbService.GetMRBById(id, bypassCache);
return Ok(mrb);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get MRB by Id, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBbyId";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/getByTitle")]
public async Task<IActionResult> GetMRBByTitle(string title, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get an MRB by Title");
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty");
MRB mrb = await _mrbService.GetMRBByTitle(title, bypassCache);
return Ok(mrb);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get MRB by title, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBbyTitle";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPut]
[Route("mrb")]
public async Task<IActionResult> UpdateMRB(MRB mrb) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to update an MRB");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
await _mrbService.UpdateMRB(mrb);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot update MRB, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UpdateMRB";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrbAction")]
public async Task<IActionResult> CreateMRBAction(MRBAction mrbAction) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to generate a new MRB");
if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null");
await _mrbService.CreateMRBAction(mrbAction);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot create new MRB action, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "CreateNewMRBAction";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrbAction")]
public async Task<IActionResult> GetMRBActionsForMRB(int mrbNumber, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get all MRBs");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
IEnumerable<MRBAction> mrbActions = await _mrbService.GetMRBActionsForMRB(mrbNumber, bypassCache);
return Ok(mrbActions);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get all MRBs, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBActionsForMRB";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPut]
[Route("mrbAction")]
public async Task<IActionResult> UpdateMRBAction(MRBAction mrbAction) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to update an MRB action");
if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null");
await _mrbService.UpdateMRBAction(mrbAction);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot update MRB action, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UpdateMRBAction";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpDelete]
[Route("mrbAction")]
public async Task<IActionResult> DeleteMRBAction(int mrbActionID, int mrbNumber) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to delete MRB action {mrbActionID}");
if (mrbActionID <= 0) throw new ArgumentException($"{mrbActionID} is not a valid MRB ActionID");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRBNumber");
await _mrbService.DeleteMRBAction(mrbActionID, mrbNumber);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot delete MRB action, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "DeleteMRBAction";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/attachments")]
public async Task<IActionResult> GetAttachmentsForMRB(int mrbNumber, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get MRB attachments for MRB {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
List<MRBAttachment> attachments = (await _mrbService.GetAllAttachmentsForMRB(mrbNumber, bypassCache)).ToList();
return Ok(attachments);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get attachments for MRB {mrbNumber}, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBAttachments";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("mrb/action/attachments")]
public async Task<IActionResult> GetActionAttachmentsForMRB(int mrbNumber, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get action attachments for MRB {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#");
List<MRBActionAttachment> attachments =
(await _mrbService.GetAllActionAttachmentsForMRB(mrbNumber, bypassCache)).ToList();
return Ok(attachments);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get action attachments for MRB# {mrbNumber}, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBActionAttachments";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/attach")]
public async Task<IActionResult> SaveMRBAttachment([FromForm] IEnumerable<IFormFile> files, int mrbNumber) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to save MRB attachments");
if (files is null) throw new ArgumentNullException("Files cannot be null");
if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
IEnumerable<UploadResult> uploadResults = (await _mrbService.UploadAttachments(files, mrbNumber)).ToList();
return Ok(uploadResults);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot save MRB attachments, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "SaveMRBAttachments";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/action/attach")]
public async Task<IActionResult> SaveMRBActionAttachment([FromForm] IEnumerable<IFormFile> files, int actionId) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to save MRB action attachments");
if (files is null) throw new ArgumentNullException("Files cannot be null");
if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty");
if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID");
IEnumerable<UploadResult> uploadResults = (await _mrbService.UploadActionAttachments(files, actionId)).ToList();
return Ok(uploadResults);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot save MRB action attachments, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "SaveMRBActionAttachments";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[AllowAnonymous]
[HttpGet]
[Route("mrb/attachmentFile")]
public async Task<IActionResult> GetMRBAttachmentFile(string path, string fileName) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get MRB attachment file");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (!System.IO.File.Exists(path)) throw new ArgumentException("No file exists at provided path");
if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentException("Filename cannot be null or empty");
byte[] fs = System.IO.File.ReadAllBytes(path);
const string defaultContentType = "application/octet-stream";
FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();
if (!contentTypeProvider.TryGetContentType(path, out string? contentType)) {
contentType = defaultContentType;
}
return new FileContentResult(fs, contentType) {
FileDownloadName = fileName
};
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get MRB attachment file, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBAttachmentFile";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[AllowAnonymous]
[HttpGet]
[Route("mrb/actions/csvFile")]
public async Task<IActionResult> GetMRBActionsCsvFile(int mrbNumber) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get MRB actions CSC file");
if (!(await _mrbService.MRBNumberIsValid(mrbNumber)))
throw new ArgumentException($"{mrbNumber} is not a valid MRB#");
string path = $"{_mrbAttachmentPath}\\{mrbNumber}\\mrb{mrbNumber}Actions.csv";
await _mrbService.ConvertActionsToCsvFile(mrbNumber, path);
byte[] fs = System.IO.File.ReadAllBytes(path);
const string defaultContentType = "application/octet-stream";
FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();
if (!contentTypeProvider.TryGetContentType(path, out string? contentType)) {
contentType = defaultContentType;
}
return new FileContentResult(fs, contentType) {
FileDownloadName = $"mrb{mrbNumber}Actions.csv"
};
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get MRB actions CSC file, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBActionsCSVFile";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpDelete]
[Route("mrb/attach")]
public async Task<IActionResult> DeleteMRBAttachment(MRBAttachment attachment) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to delete MRB attachment");
if (attachment is null) throw new ArgumentNullException("Attachment cannot be null");
await _mrbService.DeleteAttachment(attachment);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get MRB attachment file, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "DeleteMRBAttachment";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/notify/new-approvals")]
public async Task<IActionResult> NotifyNewApprovals(MRB mrb) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify new approvers");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
await _mrbService.NotifyNewApprovals(mrb);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify new approvers, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyNewMRBApprovers";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/notify/approvers")]
public async Task<IActionResult> NotifyApprovers(MRBNotification notification) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify approvers");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
await _mrbService.NotifyApprovers(notification);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify approvers, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyMRBApprovers";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/notify/originator")]
public async Task<IActionResult> NotifyOriginator(MRBNotification notification) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify originator");
if (notification is null) throw new ArgumentNullException("MRBNotification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("Message cannot be null or empty");
await _mrbService.NotifyOriginator(notification);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify originator, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyMRBOriginator";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpPost]
[Route("mrb/notify/qa-pre-approver")]
public async Task<IActionResult> NotifyQAPreApprover(MRBNotification notification) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify QA pre approver");
if (notification is null) throw new ArgumentNullException("MRBNotification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("Message cannot be null or empty");
await _mrbService.NotifyQAPreApprover(notification);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify QA pre approver, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyQAPreApprover";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,223 @@
using MesaFabApproval.API.Services;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Controllers;
[ApiController]
public class UserController : ControllerBase {
private readonly ILogger<UserController> _logger;
private readonly IMonInWorkerClient _monInClient;
private readonly IMemoryCache _cache;
private readonly IUserService _userService;
public UserController(ILogger<UserController> logger,
IMonInWorkerClient monInClient,
IMemoryCache cache,
IUserService userService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
}
[HttpGet]
[Route("/user/loginId")]
[Authorize]
public async Task<IActionResult> GetUserByLoginId(string loginId) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get user by LoginID");
if (string.IsNullOrWhiteSpace(loginId))
throw new ArgumentException("LoginID cannot be null or empty");
User? user = _cache.Get<User>($"user{loginId}");
if (user is null) {
user = await _userService.GetUserByLoginId(loginId);
_cache.Set($"user{loginId}", user, DateTimeOffset.Now.AddDays(1));
}
if (user is not null) return Ok(user);
throw new Exception($"User with LoginID {loginId} not found");
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get user by LoginID, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetUserByLoginId";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("/user/userId")]
[Authorize]
public async Task<IActionResult> GetUserByUserId(int userId) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get user by LoginID");
if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID");
User? user = _cache.Get<User>($"user{userId}");
if (user is null) {
user = await _userService.GetUserByUserId(userId);
_cache.Set($"user{userId}", user, DateTimeOffset.Now.AddDays(1));
}
if (user is not null) return Ok(user);
throw new Exception($"User with UserID {userId} not found");
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get user by User ID, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetUserByUserId";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("/users/active")]
[Authorize]
public async Task<IActionResult> GetAllActiveUsers() {
DateTime start = DateTime.Now;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get all active users");
IEnumerable<User>? activeUsers = _cache.Get<IEnumerable<User>>($"activeUsers");
if (activeUsers is null) {
activeUsers = await _userService.GetAllActiveUsers();
_cache.Set($"activeUsers", activeUsers, DateTimeOffset.Now.AddDays(1));
}
if (activeUsers is not null) return Ok(activeUsers);
throw new Exception($"No active users found");
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get all active users, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetAllActiveUsers";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
[HttpGet]
[Route("/approver")]
[Authorize]
public async Task<IActionResult> GetApproverUserIdsForSubRoleCategoryItem(string subRoleCategoryItem) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get approver user IDs");
if (string.IsNullOrWhiteSpace(subRoleCategoryItem))
throw new ArgumentException("SubRoleCategoryItem cannot be null or empty");
IEnumerable<int>? approverUserIds = _cache.Get<IEnumerable<int>>($"approvers{subRoleCategoryItem}");
if (approverUserIds is null) {
approverUserIds = await _userService.GetApproverUserIdsBySubRoleCategoryItem(subRoleCategoryItem);
_cache.Set($"approvers{subRoleCategoryItem}", approverUserIds, DateTimeOffset.Now.AddDays(1));
}
if (approverUserIds is not null) return Ok(approverUserIds);
throw new Exception($"Approvers for SubRoleCategoryItem {subRoleCategoryItem} not found");
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = $"Invalid argument. {ex.Message}";
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get approver user IDs, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetApproverUserIds";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
if (isArgumentError) {
_logger.LogWarning(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Ok);
} else if (isInternalError) {
_logger.LogError(errorMessage);
_monInClient.PostStatus(metricName, StatusValue.Critical);
} else {
_monInClient.PostStatus(metricName, StatusValue.Ok);
}
}
}
}

View File

@ -0,0 +1,9 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>", Scope = "member", Target = "~M:MesaFabApprovalAPI.Services.AuthenticationService.AuthenticateUser(MesaFabApproval.Shared.Models.AuthAttempt)~System.Threading.Tasks.Task{MesaFabApproval.Shared.Models.LoginResult}")]
[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>", Scope = "member", Target = "~M:MesaFabApprovalAPI.Services.AuthenticationService.AttemptLocalUserAuth(System.Security.Principal.WindowsIdentity)~System.Threading.Tasks.Task{MesaFabApproval.Shared.Models.LoginResult}")]

View File

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>0b98e1f2-95ed-4edd-8149-58cce51ca059</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="Dapper.Contrib" Version="2.0.78" />
<PackageReference Include="dotenv.net" Version="3.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="NLog" Version="5.2.8" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.0" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MesaFabApproval.Shared\MesaFabApproval.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.DirectoryServices.AccountManagement">
<HintPath>..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.DirectoryServices.AccountManagement.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,93 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace MesaFabApproval.Models;
public record AppSettings(string Company,
string DbConnectionString,
string JwtAudience,
string JwtIssuer,
string JwtKey,
string MrbAttachmentPath,
string PcrbAttachmentPath,
bool ShouldSendEmail,
string SiteBaseUrl,
string WorkingDirectoryName) {
public override string ToString() {
string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings);
return result;
}
public static AppSettings Get(IConfigurationRoot configurationRoot) {
AppSettings? result;
try {
#pragma warning disable IL3050, IL2026
result = configurationRoot.Get<AppSettings>() ?? throw new Exception();
#pragma warning restore IL3050, IL2026
} catch (Exception) {
List<string> paths = [];
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) {
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider)
continue;
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider)
continue;
paths.Add(physicalFileProvider.Root);
}
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
}
return result;
}
internal static void SetEnvironmentVariables(WebApplicationBuilder builder) {
builder.Configuration.AddUserSecrets<Program>();
AppSettings appSettings = Get(builder.Configuration);
Environment.SetEnvironmentVariable("FabApprovalDbConnectionString", appSettings.DbConnectionString);
Environment.SetEnvironmentVariable("FabApprovalJwtAudience", appSettings.JwtAudience);
Environment.SetEnvironmentVariable("FabApprovalJwtIssuer", appSettings.JwtIssuer);
Environment.SetEnvironmentVariable("FabApprovalJwtKey", appSettings.JwtKey);
Environment.SetEnvironmentVariable("FabApprovalMrbAttachmentPath", appSettings.MrbAttachmentPath);
Environment.SetEnvironmentVariable("FabApprovalPcrbAttachmentPath", appSettings.PcrbAttachmentPath);
Environment.SetEnvironmentVariable("FabApprovalShouldSendEmail", appSettings.ShouldSendEmail.ToString());
Environment.SetEnvironmentVariable("NewFabApprovalBaseUrl", appSettings.SiteBaseUrl);
}
internal static AppSettings LoadEnvironmentVariables() {
AppSettings result;
string dbConnectionString = Environment.GetEnvironmentVariable("FabApprovalDbConnectionString") ??
throw new ArgumentNullException("FabApprovalDbConnectionString environment variable not found");
string jwtAudience = Environment.GetEnvironmentVariable("FabApprovalJwtAudience") ??
throw new ArgumentNullException("FabApprovalJwtAudience environment variable not found");
string jwtIssuer = Environment.GetEnvironmentVariable("FabApprovalJwtIssuer") ??
throw new ArgumentNullException("FabApprovalJwtIssuer environment variable not found");
string jwtKey = Environment.GetEnvironmentVariable("FabApprovalJwtKey") ??
throw new ArgumentNullException("FabApprovalJwtKey environment variable not found");
string mrbAttachmentPath = Environment.GetEnvironmentVariable("FabApprovalMrbAttachmentPath") ??
throw new ArgumentNullException("FabApprovalMrbAttachmentPath environment variable not found");
string pcrbAttachmentPath = Environment.GetEnvironmentVariable("FabApprovalPcrbAttachmentPath") ??
throw new ArgumentNullException("FabApprovalPcrbAttachmentPath environment variable not found");
if (!bool.TryParse(Environment.GetEnvironmentVariable("FabApprovalShouldSendEmail"), out bool shouldSendEmail))
throw new ArgumentNullException("FabApprovalShouldSendEmail environment variable not found");
string siteBaseUrl = Environment.GetEnvironmentVariable("NewFabApprovalBaseUrl") ??
throw new ArgumentNullException("FabApprovalBaseUrl environment variable not found");
result = new("Infineon Technologies Americas Corp.",
DbConnectionString: dbConnectionString,
JwtAudience: jwtAudience,
JwtIssuer: jwtIssuer,
JwtKey: jwtKey,
MrbAttachmentPath: mrbAttachmentPath,
PcrbAttachmentPath: pcrbAttachmentPath,
ShouldSendEmail: shouldSendEmail,
SiteBaseUrl: siteBaseUrl,
"IFXApps");
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext {
}

View File

@ -0,0 +1,157 @@
using System.Diagnostics;
using System.Net.Mail;
using System.Text;
using dotenv.net;
using MesaFabApproval.API.Clients;
using MesaFabApproval.API.Services;
using MesaFabApproval.Models;
using MesaFabApproval.Shared.Services;
using MesaFabApprovalAPI.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.HttpLogging;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using NLog.Extensions.Logging;
using NLog.Web;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
DotEnv.Load();
builder.Logging.ClearProviders();
builder.Logging.SetMinimumLevel(LogLevel.Trace);
builder.Logging.AddNLog();
/*builder.Services.AddHttpLogging(o => {
o.LoggingFields = HttpLoggingFields.All;
o.RequestHeaders.Add("bearer");
o.MediaTypeOptions.AddText("application/javascript");
o.RequestBodyLogLimit = 4096;
o.ResponseBodyLogLimit = 4096;
o.CombineLogs = true;
});*/
builder.Services.AddMemoryCache();
if (Debugger.IsAttached) {
string? jwtIssuer = Environment.GetEnvironmentVariable("FabApprovalJwtIssuer");
if (string.IsNullOrEmpty(jwtIssuer)) {
AppSettings.SetEnvironmentVariables(builder);
}
}
AppSettings appSettings = AppSettings.LoadEnvironmentVariables();
builder.Services.AddSingleton(_ => appSettings);
builder.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options => {
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
ValidIssuer = appSettings.JwtIssuer,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(appSettings.JwtKey)),
ClockSkew = TimeSpan.Zero
};
});
builder.Services.AddAuthorization(options => {
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
builder.Services.AddHttpClient();
builder.Services.AddCors(options => {
options.AddDefaultPolicy(options => {
options.AllowAnyOrigin();
options.AllowAnyMethod();
options.AllowAnyHeader();
});
});
builder.Services.AddScoped<IDbConnectionService, DbConnectionService>();
builder.Services.AddScoped<IDalService, DalService>();
builder.Services.AddScoped<SmtpClient>((serviceProvider) => {
return new SmtpClient("mailrelay-external.infineon.com");
});
builder.Services.AddScoped<ISmtpClientWrapper, SmtpClientWrapper>();
builder.Services.AddScoped<ICustomerService, CustomerService>();
builder.Services.AddScoped<ICAService, CAService>();
builder.Services.AddScoped<IECNService, ECNService>();
builder.Services.AddScoped<ISmtpService, SmtpService>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IMonInWorkerClient, MonInWorkerClient>();
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
builder.Services.AddScoped<IMRBService, MRBService>();
builder.Services.AddScoped<IPCRBService, PCRBService>();
builder.Services.AddScoped<IApprovalService, ApprovalService>();
builder.Services.AddControllers();
#if DEBUG
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "Mesa Fab Approval API",
Version = "v1"
});
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
In = ParameterLocation.Header,
Description = "Please insert JWT with Bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
});
#endif
WebApplication app = builder.Build();
if (Debugger.IsAttached)
app.Services.GetRequiredService<IApprovalService>();
app.UseCors();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) {
app.UseSwagger();
app.UseSwaggerUI();
}
// app.UseHttpLogging();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
NLog.LogManager.Flush();
NLog.LogManager.Shutdown();

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<DeleteExistingFiles>true</DeleteExistingFiles>
<ExcludeApp_Data>false</ExcludeApp_Data>
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>bin\Release\net8.0\publish\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<SiteUrlToLaunchAfterPublish />
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<ProjectGuid>852e528d-015a-43b5-999d-f281e3359e5e</ProjectGuid>
<SelfContained>true</SelfContained>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,320 @@
using System.Text;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Utilities;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface IApprovalService {
Task<int> GetRoleIdForRoleName(string roleName);
Task<IEnumerable<SubRole>> GetSubRolesForSubRoleName(string subRoleName, int roleId);
Task<IEnumerable<User>> GetApprovalGroupMembers(int subRoleId);
Task CreateApproval(Approval approval);
Task UpdateApproval(Approval approval);
Task Approve(Approval approval);
Task Deny(Approval approval);
Task<IEnumerable<Approval>> GetApprovalsForIssueId(int issueId, bool bypassCache);
Task<IEnumerable<Approval>> GetApprovalsForUserId(int userId, bool bypassCache);
}
public class ApprovalService : IApprovalService {
private readonly ILogger<ApprovalService> _logger;
private readonly IMemoryCache _cache;
private readonly IDalService _dalService;
private readonly IUserService _userService;
public ApprovalService(ILogger<ApprovalService> logger, IMemoryCache cache, IDalService dalService, IUserService userService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_dalService = dalService ?? throw new ArgumentNullException("IDalService not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
}
public async Task CreateApproval(Approval approval) {
try {
_logger.LogInformation("Attempting to generate new Approval");
if (approval is null) throw new ArgumentNullException("Approval cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into Approval (IssueID, RoleName, SubRole, UserID, SubRoleID, ItemStatus, ");
queryBuilder.Append("AssignedDate, DocumentTypeID, DisplayDeniedDocument, Step, TaskID) ");
queryBuilder.Append($"values ({approval.IssueID}, '{approval.RoleName}', '{approval.SubRole}', {approval.UserID}, ");
queryBuilder.Append($"{approval.SubRoleID}, 0, '{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"3, 0, {approval.Step}, {approval.TaskID});");
int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsCreated <= 0) throw new Exception("Unable to insert approval in database");
await GetApprovalsForIssueId(approval.IssueID, true);
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to create new Approval. Exception: {ex.Message}");
throw;
}
}
public async Task<IEnumerable<Approval>> GetApprovalsForIssueId(int issueId, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get all approvals for issue {issueId}");
if (issueId <= 0) throw new ArgumentException($"{issueId} is not a valid issue ID");
IEnumerable<Approval>? approvals = new List<Approval>();
if (!bypassCache)
approvals = _cache.Get<IEnumerable<Approval>>($"approvals{issueId}");
if (approvals is null || approvals.Count() == 0) {
StringBuilder queryBuilder = new();
queryBuilder.Append("select a.*, src.SubRoleCategoryItem from Approval a ");
queryBuilder.Append("join SubRole sr on a.SubRoleID=sr.SubRoleID ");
queryBuilder.Append("join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID ");
queryBuilder.Append($"where a.IssueID={issueId}");
approvals = (await _dalService.QueryAsync<Approval>(queryBuilder.ToString())).ToList();
foreach (Approval approval in approvals) {
int successfulUpdates = 0;
User? user = await _userService.GetUserByUserId(approval.UserID);
if (user is not null) {
approval.User = user;
successfulUpdates++;
}
if (approval.ItemStatus < 0)
approval.StatusMessage = "Denied";
if (approval.ItemStatus == 0)
approval.StatusMessage = "Assigned";
if (approval.ItemStatus > 0)
approval.StatusMessage = "Approved";
}
_cache.Set($"approvals{issueId}", approvals, DateTimeOffset.Now.AddMinutes(5));
}
return approvals;
} catch (Exception ex) {
_logger.LogError($"Unable to fetch approvals for issue {issueId}, because {ex.Message}");
throw;
}
}
public async Task<int> GetRoleIdForRoleName(string roleName) {
try {
_logger.LogInformation($"Attempting to get role ID by name");
if (string.IsNullOrWhiteSpace(roleName))
throw new ArgumentException("Role name cannot be null or empty");
int roleId = _cache.Get<int>($"role{roleName}");
if (roleId <= 0) {
string sql = $"select RoleID from Role where RoleName = '{roleName}'";
roleId = (await _dalService.QueryAsync<int>(sql)).ToList().FirstOrDefault();
if (roleId > 0)
_cache.Set($"role{roleName}", roleId, DateTimeOffset.Now.AddDays(1));
}
if (roleId <= 0)
throw new Exception($"Unable to find role with name {roleName}");
return roleId;
} catch (Exception ex) {
_logger.LogError($"Unable to find role ID, because {ex.Message}");
throw;
}
}
public async Task<IEnumerable<SubRole>> GetSubRolesForSubRoleName(string subRoleName, int roleId) {
try {
_logger.LogInformation($"Attempting to get sub role ID by name for role ID {roleId}");
if (string.IsNullOrWhiteSpace(subRoleName))
throw new ArgumentException("sub role name cannot be null or empty");
if (roleId <= 0) throw new ArgumentException($"{roleId} is not a valid role ID");
IEnumerable<SubRole>? subRoles = _cache.Get<IEnumerable<SubRole>>($"subRoles{subRoleName}");
if (subRoles is null || subRoles.Count() <= 0) {
StringBuilder queryBuilder = new();
queryBuilder.Append("select src.SubRoleCategoryID, sr.SubRole as SubRoleName, src.SubRoleCategoryItem, sr.SubRoleID ");
queryBuilder.Append("from SubRole sr join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID ");
queryBuilder.Append($"where sr.RoleID={roleId} and sr.SubRole='{subRoleName}' and sr.Inactive=0");
subRoles = (await _dalService.QueryAsync<SubRole>(queryBuilder.ToString())).ToList();
if (subRoles is not null && subRoles.Count() > 0)
_cache.Set($"subRole{subRoleName}", subRoles, DateTimeOffset.Now.AddDays(1));
}
if (subRoles is null || subRoles.Count() <= 0)
throw new Exception($"Unable to find sub role with name {subRoleName} for role {roleId}");
return subRoles;
} catch (Exception ex) {
_logger.LogError($"Unable to find sub roles, because {ex.Message}");
throw;
}
}
public async Task<IEnumerable<User>> GetApprovalGroupMembers(int subRoleId) {
try {
_logger.LogInformation($"Attempting to get members of sub role {subRoleId}");
if (subRoleId <= 0) throw new ArgumentException($"{subRoleId} is not a valid sub role ID");
List<User>? members = _cache.Get<List<User>>($"approvalMembers{subRoleId}");
if (members is null || members.Count() <= 0) {
IEnumerable<int>? memberIds = _cache.Get<IEnumerable<int>>($"approvalMemberIds{subRoleId}");
if (memberIds is null) {
string sql = $"select UserID from UserSubRole where SubRoleID = {subRoleId};";
memberIds = await _dalService.QueryAsync<int>(sql);
if (memberIds is null || memberIds.Count() <= 0)
throw new Exception($"No members found in sub role {subRoleId}");
_cache.Set($"approvalMemberIds{subRoleId}", memberIds, DateTimeOffset.Now.AddMinutes(5));
}
members = new();
foreach (int id in memberIds) {
User member = await _userService.GetUserByUserId(id);
members.Add(member);
}
if (members.Count() <= 0) throw new Exception("No users found with IDs matching those found in SubRole");
_cache.Set($"approvalMembers{subRoleId}", members, DateTimeOffset.Now.AddMinutes(5));
}
return members;
} catch (Exception ex) {
_logger.LogError($"Unable to get sub role {subRoleId} members, because {ex.Message}");
throw;
}
}
public async Task<IEnumerable<Approval>> GetApprovalsForUserId(int userId, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get approvals for user ID {userId}");
if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID");
IEnumerable<Approval>? approvals = null;
if (!bypassCache) approvals = _cache.Get<IEnumerable<Approval>>($"approvalMembers{userId}");
if (approvals is null) {
StringBuilder queryBuilder = new();
queryBuilder.Append($"select a.*, src.SubRoleCategoryItem from Approval a ");
queryBuilder.Append("join SubRole sr on a.SubRoleID=sr.SubRoleID ");
queryBuilder.Append("join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID ");
queryBuilder.Append($"where UserID={userId} and ");
queryBuilder.Append($"((CompletedDate >= '{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}') or ");
queryBuilder.Append($"(CompletedDate is null));");
string sql = queryBuilder.ToString();
approvals = (await _dalService.QueryAsync<Approval>(sql)).ToList();
_cache.Set($"approvalMembers{userId}", approvals, DateTimeOffset.Now.AddHours(1));
}
return approvals;
} catch (Exception ex) {
_logger.LogError($"Unable to get approvals for user ID {userId}, because {ex.Message}");
throw;
}
}
public async Task UpdateApproval(Approval approval) {
try {
_logger.LogInformation("Attempting to update an approval");
if (approval is null) throw new ArgumentNullException("Approval cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update Approval set IssueID={approval.IssueID}, RoleName='{approval.RoleName}', ");
queryBuilder.Append($"SubRole='{approval.SubRole}', UserID={approval.UserID}, SubRoleID={approval.SubRoleID}, ");
queryBuilder.Append($"ItemStatus={Convert.ToInt32(approval.ItemStatus)}, Step={approval.Step}, ");
queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"AssignedDate='{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"CompletedDate='{approval.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"Comments='{approval.Comments.Replace("'", "''")}', ");
queryBuilder.Append($"TaskID={approval.TaskID} ");
queryBuilder.Append($"where ApprovalID={approval.ApprovalID};");
int rowsUpdated = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsUpdated <= 0) throw new Exception("Unable to update approval in database");
} catch (Exception ex) {
_logger.LogError($"Approval update failed, because {ex.Message}");
throw;
}
}
public async Task Approve(Approval approval) {
try {
_logger.LogInformation("Attempting to submit approval");
if (approval is null) throw new ArgumentNullException("Approval cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update Approval set IssueID={approval.IssueID}, RoleName='{approval.RoleName}', ");
queryBuilder.Append($"SubRole='{approval.SubRole}', UserID={approval.UserID}, SubRoleID={approval.SubRoleID}, ");
queryBuilder.Append($"ItemStatus=1, Step={approval.Step}, ");
if (approval.NotifyDate < DateTimeUtilities.MIN_DT)
approval.NotifyDate = DateTimeUtilities.MIN_DT;
queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"AssignedDate='{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"CompletedDate='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"Comments='{approval.Comments}', ");
queryBuilder.Append($"TaskID={approval.TaskID} ");
queryBuilder.Append($"where ApprovalID={approval.ApprovalID};");
int rowsUpdated = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsUpdated <= 0) throw new Exception("Unable to submit approval in database");
} catch (Exception ex) {
_logger.LogError($"Approval failed, because {ex.Message}");
throw;
}
}
public async Task Deny(Approval approval) {
try {
_logger.LogInformation("Attempting to deny approval");
if (approval is null) throw new ArgumentNullException("Approval cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update Approval set IssueID={approval.IssueID}, RoleName='{approval.RoleName}', ");
queryBuilder.Append($"SubRole='{approval.SubRole}', UserID={approval.UserID}, SubRoleID={approval.SubRoleID}, ");
queryBuilder.Append($"ItemStatus=-1, Step={approval.Step}, ");
if (approval.NotifyDate < DateTimeUtilities.MIN_DT)
approval.NotifyDate = DateTimeUtilities.MIN_DT;
queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"AssignedDate='{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"CompletedDate='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"Comments='{approval.Comments}', ");
queryBuilder.Append($"TaskID={approval.TaskID} ");
queryBuilder.Append($"where ApprovalID={approval.ApprovalID};");
int rowsUpdated = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsUpdated <= 0) throw new Exception("Unable to deny approval in database");
} catch (Exception ex) {
_logger.LogError($"Approval denial failed, because {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,251 @@
using System.DirectoryServices.AccountManagement;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Authentication;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using MesaFabApproval.API.Services;
using MesaFabApproval.Models;
using MesaFabApproval.Shared.Models;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.IdentityModel.Tokens;
namespace MesaFabApprovalAPI.Services;
public interface IAuthenticationService {
public Task<LoginResult> AuthenticateUser(AuthAttempt login);
public Task<LoginResult> AttemptLocalUserAuth(WindowsIdentity identity);
public AuthTokens GenerateAuthTokens(AuthAttempt authAttempt, IEnumerable<string> roles);
public Task<LoginResult> RefreshAuthTokens(AuthAttempt authAttempt);
}
public class AuthenticationService : IAuthenticationService {
private readonly ILogger<AuthenticationService> _logger;
private readonly IMemoryCache _cache;
private readonly IUserService _userService;
private readonly string _jwtIssuer;
private readonly string _jwtAudience;
private readonly string _jwtKey;
public AuthenticationService(ILogger<AuthenticationService> logger, IMemoryCache cache, IUserService userService, AppSettings appSettings) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
_jwtKey = appSettings.JwtKey;
_jwtIssuer = appSettings.JwtIssuer;
_jwtAudience = appSettings.JwtAudience;
}
public async Task<LoginResult> AuthenticateUser(AuthAttempt login) {
try {
_logger.LogInformation("Attempting to authenticate user");
if (login is null) throw new ArgumentNullException("Login cannot be null");
string domain = "infineon.com";
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain)) {
bool isValid = pc.ValidateCredentials(login.LoginID, login.Password);
if (isValid) {
User? user = _cache.Get<User>($"user{login.LoginID}");
if (user is null) {
user = await _userService.GetUserByLoginId(login.LoginID);
_cache.Set<User>($"user{login.LoginID}", user, DateTimeOffset.Now.AddDays(1));
}
List<string> roles = new();
if (user.IsManager) roles.Add("manager");
if (user.IsAdmin) roles.Add("admin");
AuthTokens tokens = GenerateAuthTokens(login, roles);
return new LoginResult {
IsAuthenticated = true,
AuthTokens = tokens,
User = user
};
} else {
return new LoginResult() {
IsAuthenticated = false,
AuthTokens = new() {
JwtToken = "",
RefreshToken = ""
},
User = null
};
}
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to authenticate user. Exception: {ex.Message}");
throw;
}
}
public async Task<LoginResult> AttemptLocalUserAuth(WindowsIdentity identity) {
try {
_logger.LogInformation("Attempting to authenticate local Windows system user");
if (identity is null) throw new ArgumentNullException("WindowsIdentity cannot be null");
User user = await _userService.GetUserByLoginId(identity.Name);
List<string> roles = new();
if (user.IsManager) roles.Add("manager");
if (user.IsAdmin) roles.Add("admin");
AuthAttempt authAttempt = new() {
LoginID = user.LoginID,
};
AuthTokens tokens = GenerateAuthTokens(authAttempt, roles);
return new LoginResult {
IsAuthenticated = true,
AuthTokens = tokens,
User = user
};
} catch (Exception ex) {
_logger.LogError($"Unable to authenticate local Windows system user, because {ex.Message}");
throw;
}
}
public AuthTokens GenerateAuthTokens(AuthAttempt authAttempt, IEnumerable<string> roles) {
try {
_logger.LogInformation("Attempting to generate JWT");
if (authAttempt is null) throw new ArgumentNullException("AuthAttempt cannot be null");
if (string.IsNullOrWhiteSpace(authAttempt.LoginID)) throw new ArgumentException("UserName cannot be null or empty");
if (roles is null) throw new ArgumentNullException("roles cannot be null");
byte[] key = Encoding.ASCII.GetBytes(_jwtKey);
List<Claim> claims = new() {
new Claim(nameof(authAttempt.LoginID), authAttempt.LoginID)
};
foreach (string role in roles) {
claims.Add(new Claim(ClaimTypes.Role, role));
}
ClaimsIdentity identity = new ClaimsIdentity(claims);
SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor {
Issuer = _jwtIssuer,
Audience = _jwtAudience,
Subject = identity,
NotBefore = DateTime.Now,
Expires = DateTime.Now.AddHours(8),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
JwtSecurityToken token = tokenHandler.CreateJwtSecurityToken(tokenDescriptor);
string jwt = tokenHandler.WriteToken(token);
string refreshToken = GenerateRefreshToken();
List<string>? refreshTokensForUser = _cache.Get<List<string>>(authAttempt.LoginID);
if (refreshTokensForUser is null)
refreshTokensForUser = new List<string>();
if (refreshTokensForUser.Count > 9)
refreshTokensForUser.RemoveRange(9, refreshTokensForUser.Count - 9);
refreshTokensForUser.Insert(0, refreshToken);
_cache.Set<List<string>>(authAttempt.LoginID, refreshTokensForUser, DateTimeOffset.Now.AddHours(4));
return new AuthTokens {
JwtToken = jwt,
RefreshToken = refreshToken
};
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to generate JWT. Exception: {ex.Message}");
throw;
}
}
public async Task<LoginResult> RefreshAuthTokens(AuthAttempt authAttempt) {
try {
_logger.LogInformation("Attempting to refresh auth tokens");
if (authAttempt is null) throw new ArgumentNullException("AuthAttempt cannot be null");
if (authAttempt.AuthTokens is null) throw new ArgumentNullException("AuthTokens cannot be null");
bool refreshTokenIsValid = IsRefreshTokenValid(authAttempt.LoginID, authAttempt.AuthTokens.RefreshToken);
if (refreshTokenIsValid) {
User? user = _cache.Get<User>($"user{authAttempt.LoginID}");
if (user is null) {
user = await _userService.GetUserByLoginId(authAttempt.LoginID);
_cache.Set<User>($"user{authAttempt.LoginID}", user, DateTimeOffset.Now.AddDays(1));
}
List<string> roles = new();
if (user.IsManager) roles.Add("manager");
if (user.IsAdmin) roles.Add("admin");
AuthTokens refreshedTokens = GenerateAuthTokens(authAttempt, roles);
LoginResult loginResult = new LoginResult() {
IsAuthenticated = true,
AuthTokens = refreshedTokens,
User = user
};
return loginResult;
} else {
throw new AuthenticationException("Invalid refresh token");
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to refresh auth tokens. Exception: {ex.Message}");
throw;
}
}
private string GenerateRefreshToken() {
byte[] randomNumber = new byte[32];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) {
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
}
private bool IsRefreshTokenValid(string loginId, string refreshToken) {
try {
_logger.LogInformation("Attempting to determine if refresh token is valid");
if (string.IsNullOrWhiteSpace(loginId)) throw new ArgumentNullException("LoginID cannot be null or empty");
if (string.IsNullOrWhiteSpace(refreshToken))
throw new ArgumentNullException("Refresh token cannot be null or empty");
List<string>? cachedRefreshTokensForUser = _cache.Get<List<string>>(loginId);
if (cachedRefreshTokensForUser is null || !cachedRefreshTokensForUser.Contains(refreshToken)) {
_logger.LogInformation($"Could not find cached refresh tokens for user {loginId}");
return false;
}
return true;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to validate refresh token. Exception: {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,46 @@
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface ICAService {
Task<bool> IsValidCANumber(int number);
}
public class CAService : ICAService {
private readonly ILogger<CAService> _logger;
private readonly IMemoryCache _cache;
private readonly IDalService _dalService;
public CAService(ILogger<CAService> logger, IMemoryCache cache, IDalService dalService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_dalService = dalService ?? throw new ArgumentNullException("IDalService not injected");
}
public async Task<bool> IsValidCANumber(int number) {
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid CA#");
if (number <= 0) return false;
IEnumerable<int> caNumbers = _cache.Get<IEnumerable<int>>("caNumbers") ?? new HashSet<int>();
if (caNumbers.Contains(number)) return true;
string sql = $"select count(CANo) as count from _8DCorrectiveAction where CANo={number}";
int rowsReturned = (await _dalService.QueryAsync<int>(sql)).FirstOrDefault();
if (rowsReturned > 0) {
caNumbers.Append(number);
_cache.Set("caNumbers", caNumbers);
return true;
}
return false;
} catch (Exception ex) {
_logger.LogError($"Unable to determine if {number} is a valid CA#, because {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,38 @@
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface ICustomerService {
Task<IEnumerable<string>> GetCustomerNames();
}
public class CustomerService : ICustomerService {
private readonly ILogger<CustomerService> _logger;
private readonly IDalService _dalService;
private readonly IMemoryCache _cache;
public CustomerService(ILogger<CustomerService> logger, IDalService dalService, IMemoryCache cache) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_dalService = dalService ?? throw new ArgumentNullException("IDalService not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
}
public async Task<IEnumerable<string>> GetCustomerNames() {
try {
_logger.LogInformation("Attempting to get customer names");
IEnumerable<string>? customerNames = _cache.Get<IEnumerable<string>>("allCustomerNames");
if (customerNames is null) {
string sql = "select ProductFamily from ProductFamilies;";
customerNames = (await _dalService.QueryAsync<string>(sql)).ToList();
}
return customerNames;
} catch (Exception ex) {
_logger.LogError($"Unable to get customer names, because {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,89 @@
using System.Data;
using Dapper;
namespace MesaFabApproval.API.Services;
public interface IDalService {
Task<IEnumerable<T>> QueryAsync<T>(string sql);
Task<int> ExecuteAsync(string sql);
}
public class DalService : IDalService {
private static readonly int RETRIES = 3;
private static readonly int BACKOFF_SECONDS_INTERVAL = 30;
private readonly ILogger<DalService> _logger;
private readonly IDbConnectionService _dbConnectionService;
public DalService(IDbConnectionService dbConnectionService, ILogger<DalService> logger) {
_dbConnectionService = dbConnectionService ??
throw new ArgumentNullException("IDbConnectionService not injected");
_logger = logger ??
throw new ArgumentNullException("ILogger not injected");
}
public async Task<IEnumerable<T>> QueryAsync<T>(string sql) {
if (sql is null) throw new ArgumentNullException("sql cannot be null");
int remainingRetries = RETRIES;
bool queryWasSuccessful = false;
Exception exception = null;
IEnumerable<T> result = new List<T>();
while (!queryWasSuccessful && remainingRetries > 0) {
int backoffSeconds = (RETRIES - remainingRetries--) * BACKOFF_SECONDS_INTERVAL;
Task.Delay(backoffSeconds * 1000).Wait();
try {
_logger.LogInformation($"Attempting to perform query with {sql}. Remaining retries: {remainingRetries}");
using (IDbConnection conn = _dbConnectionService.GetConnection()) {
result = await conn.QueryAsync<T>(sql);
}
queryWasSuccessful = true;
} catch (Exception ex) {
_logger.LogError($"An exception occurred while attempting to perform a query. Exception: {ex.Message}");
exception = ex;
}
}
if (!queryWasSuccessful && exception is not null) {
throw exception;
}
return result;
}
public async Task<int> ExecuteAsync(string sql) {
if (sql is null) throw new ArgumentNullException("sql cannot be null");
int remainingRetries = RETRIES;
bool queryWasSuccessful = false;
Exception exception = null;
int rowsAffected = 0;
while (!queryWasSuccessful && remainingRetries > 0) {
int backoffSeconds = (RETRIES - remainingRetries--) * BACKOFF_SECONDS_INTERVAL;
Task.Delay(backoffSeconds * 1000).Wait();
try {
_logger.LogInformation($"Attempting to execute {sql}. Remaining retries: {remainingRetries}");
using (IDbConnection conn = _dbConnectionService.GetConnection()) {
rowsAffected = await conn.ExecuteAsync(sql);
}
queryWasSuccessful = true;
} catch (Exception ex) {
_logger.LogError($"An exception occurred while attempting to execute a query. Exception: {ex.Message}");
exception = ex;
}
}
if (!queryWasSuccessful && exception is not null) {
throw exception;
}
return rowsAffected;
}
}

View File

@ -0,0 +1,22 @@
using System.Data;
using MesaFabApproval.Models;
using Microsoft.Data.SqlClient;
namespace MesaFabApproval.API.Services;
public interface IDbConnectionService {
IDbConnection GetConnection();
}
public class DbConnectionService : IDbConnectionService {
private readonly string _dbConnectionString;
public DbConnectionService(AppSettings appSettings) {
_dbConnectionString = appSettings.DbConnectionString;
}
public IDbConnection GetConnection() =>
new SqlConnection(_dbConnectionString);
}

View File

@ -0,0 +1,46 @@
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface IECNService {
Task<bool> IsValidECNNumber(int number);
}
public class ECNService : IECNService {
private readonly ILogger<ECNService> _logger;
private readonly IMemoryCache _cache;
private readonly IDalService _dalService;
public ECNService(ILogger<ECNService> logger, IMemoryCache cache, IDalService dalService) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_dalService = dalService ?? throw new ArgumentNullException("IDalService not injected");
}
public async Task<bool> IsValidECNNumber(int number) {
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid ECN#");
if (number <= 0) return false;
IEnumerable<int> ecnNumbers = _cache.Get<IEnumerable<int>>("ecnNumbers") ?? new HashSet<int>();
if (ecnNumbers.Contains(number)) return true;
string sql = $"select count(ECNNumber) as count from ECN where ECNNumber={number}";
int rowsReturned = (await _dalService.QueryAsync<int>(sql)).FirstOrDefault();
if (rowsReturned > 0) {
ecnNumbers.Append(number);
_cache.Set("ecnNumbers", ecnNumbers);
return true;
}
return false;
} catch (Exception ex) {
_logger.LogError($"Unable to determine if {number} is a valid ECN#, because {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,938 @@
using System.Data;
using System.Net;
using System.Net.Mail;
using System.Text;
using MesaFabApproval.API.Utilities;
using MesaFabApproval.Models;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Utilities;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface IMRBService {
Task<IEnumerable<MRB>> GetAllMRBs(bool bypassCache);
Task<bool> MRBNumberIsValid(int number);
Task<MRB> GetMRBById(int id, bool bypassCache = false);
Task<MRB> GetMRBByTitle(string title, bool bypassCache);
Task CreateNewMRB(MRB mrb);
Task UpdateMRB(MRB mrb);
Task CreateMRBAction(MRBAction mrbAction);
Task<IEnumerable<MRBAction>> GetMRBActionsForMRB(int mrbNumber, bool bypassCache);
Task UpdateMRBAction(MRBAction mrbAction);
Task DeleteMRBAction(int mrbActionID, int mrbNumber);
Task<IEnumerable<UploadResult>> UploadAttachments(IEnumerable<IFormFile> files, int mrbNumber);
Task<IEnumerable<UploadResult>> UploadActionAttachments(IEnumerable<IFormFile> files, int actionId);
Task<IEnumerable<MRBAttachment>> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache);
Task<IEnumerable<MRBActionAttachment>> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache);
Task DeleteAttachment(MRBAttachment attachment);
Task NotifyNewApprovals(MRB mrb);
Task NotifyApprovers(MRBNotification notification);
Task NotifyOriginator(MRBNotification notification);
Task NotifyQAPreApprover(MRBNotification notification);
Task DeleteMRB(int mrbNumber);
Task ConvertActionsToCsvFile(int mrbNumber, string path);
}
public class MRBService : IMRBService {
private readonly ILogger<MRBService> _logger;
private readonly IDalService _dalService;
private readonly IMemoryCache _cache;
private readonly IUserService _userService;
private readonly IApprovalService _approvalService;
private readonly ISmtpService _smtpService;
private readonly string _siteBaseUrl;
private readonly string _mrbAttachmentPath;
public MRBService(ILogger<MRBService> logger,
IDalService dalService,
IMemoryCache cache,
IUserService userService,
IApprovalService approvalService,
ISmtpService smtpService,
AppSettings appSettings) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_dalService = dalService ?? throw new ArgumentNullException("IDalService not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
_approvalService = approvalService ?? throw new ArgumentNullException("IApprovalService not injected");
_smtpService = smtpService ?? throw new ArgumentNullException("ISmtpService not injected");
_siteBaseUrl = appSettings.SiteBaseUrl;
_mrbAttachmentPath = appSettings.MrbAttachmentPath;
}
public async Task CreateNewMRB(MRB mrb) {
try {
_logger.LogInformation("Attempting to generate new MRB");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into MRB (OriginatorID, Title, SubmittedDate, ");
queryBuilder.Append("CloseDate, CancelDate, NumberOfLotsAffected, ApprovalDate, ");
queryBuilder.Append("IssueDescription, CustomerImpacted, Department, Process, Val, RMANo, ");
queryBuilder.Append("PCRBNo, SpecsImpacted, TrainingRequired, Status, StageNo, ");
queryBuilder.Append("CustomerImpactedName, ProcessECNNumber, Tool, Category) values (");
queryBuilder.Append($"{mrb.OriginatorID}, '{mrb.Title}', ");
if (mrb.SubmittedDate < DateTimeUtilities.MIN_DT)
mrb.SubmittedDate = DateTimeUtilities.MIN_DT;
queryBuilder.Append($"'{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
if (mrb.CloseDate > DateTimeUtilities.MAX_DT)
mrb.CloseDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"'{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
if (mrb.CancelDate > DateTimeUtilities.MAX_DT)
mrb.CancelDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"'{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"{mrb.NumberOfLotsAffected}, ");
if (mrb.ApprovalDate > DateTimeUtilities.MAX_DT)
mrb.ApprovalDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"'{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"'{mrb.IssueDescription}', {Convert.ToUInt32(mrb.CustomerImpacted)}, ");
queryBuilder.Append($"'{mrb.Department}', '{mrb.Process}', {mrb.Val}, {mrb.RMANo}, '{mrb.PCRBNo}', ");
queryBuilder.Append($"{Convert.ToInt32(mrb.SpecsImpacted)}, {Convert.ToInt32(mrb.TrainingRequired)}, ");
queryBuilder.Append($"'{mrb.Status}', {mrb.StageNo}, '{mrb.CustomerImpactedName}', ");
queryBuilder.Append($"{mrb.ProcessECNNumber}, '{mrb.Tool}', '{mrb.Category}');");
int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsCreated <= 0) throw new Exception("Unable to create new MRB");
mrb = await GetMRBByTitle(mrb.Title, true);
_cache.Set($"mrb{mrb.MRBNumber}", mrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"mrb{mrb.Title}", mrb, DateTimeOffset.Now.AddHours(1));
IEnumerable<MRB>? allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs");
if (allMrbs is not null) {
List<MRB> mrbList = allMrbs.ToList();
mrbList.Add(mrb);
_cache.Set("allMrbs", mrbList, DateTimeOffset.Now.AddHours(1));
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to create new MRB. Exception: {ex.Message}");
throw;
}
}
public async Task<IEnumerable<MRB>> GetAllMRBs(bool bypassCache) {
try {
_logger.LogInformation("Attempting to get all MRBs");
IEnumerable<MRB>? allMrbs = null;
if (!bypassCache) allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs");
if (allMrbs is null) {
StringBuilder queryBuilder = new();
queryBuilder.Append("select (u.FirstName + ' ' + u.LastName) as OriginatorName, m.* ");
queryBuilder.Append("from MRB m join Users u on m.OriginatorID = u.UserID;");
allMrbs = (await _dalService.QueryAsync<MRB>(queryBuilder.ToString())).ToList();
_cache.Set("allMrbs", allMrbs, DateTimeOffset.Now.AddHours(1));
}
return allMrbs;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to get all MRBs. Exception: {ex.Message}");
throw;
}
}
public async Task<bool> MRBNumberIsValid(int number) {
try {
_logger.LogInformation($"Attempting to determine if {number} is a valid MRB#");
if (number <= 0) return false;
IEnumerable<int> mrbNumbers = _cache.Get<IEnumerable<int>>("mrbNumbers") ??
new HashSet<int>();
if (mrbNumbers.Contains(number)) return true;
string sql = $"select count(MRBNumber) as count from MRB where MRBNumber={number}";
int rowsReturned = (await _dalService.QueryAsync<int>(sql)).FirstOrDefault();
if (rowsReturned > 0) {
mrbNumbers.Append(number);
_cache.Set("mrbNumbers", mrbNumbers);
return true;
}
return false;
} catch (Exception ex) {
_logger.LogError($"Unable to determine if {number} is a valid MRB#, because {ex.Message}");
throw;
}
}
public async Task<MRB> GetMRBById(int id, bool bypassCache = false) {
try {
_logger.LogInformation("Attempting to get an MRB by ID");
if (id < 0) throw new ArgumentException("Invalid MRB number");
MRB? mrb = null;
if (!bypassCache)
mrb = _cache.Get<MRB>($"mrb{id}");
if (mrb is null) {
StringBuilder queryBuilder = new();
queryBuilder.Append("select (u.FirstName + ' ' + u.LastName) as OriginatorName, m.* ");
queryBuilder.Append("from MRB m join Users u on m.OriginatorID = u.UserID ");
queryBuilder.Append($"where m.MRBNumber = {id}");
mrb = (await _dalService.QueryAsync<MRB>(queryBuilder.ToString())).FirstOrDefault();
_cache.Set($"mrb{id}", mrb, DateTimeOffset.Now.AddHours(1));
}
if (mrb is null) throw new Exception($"Unable to get MRB {id}");
return mrb;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to get an MRB. Exception: {ex.Message}");
throw;
}
}
public async Task<MRB> GetMRBByTitle(string title, bool bypassCache) {
try {
_logger.LogInformation("Attempting to get an MRB by title");
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty");
MRB? mrb = null;
if (!bypassCache) mrb = _cache.Get<MRB>($"mrb{title}");
if (mrb is null) {
StringBuilder queryBuilder = new();
queryBuilder.Append("select (u.FirstName + ' ' + u.LastName) as OriginatorName, m.* ");
queryBuilder.Append("from MRB m join Users u on m.OriginatorID = u.UserID ");
queryBuilder.Append($"where m.Title = '{title}'");
mrb = (await _dalService.QueryAsync<MRB>(queryBuilder.ToString())).FirstOrDefault();
_cache.Set($"mrb{title}", mrb, DateTimeOffset.Now.AddHours(1));
}
if (mrb is null) throw new Exception($"Unable to get MRB {title}");
return mrb;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to get an MRB. Exception: {ex.Message}");
throw;
}
}
public async Task UpdateMRB(MRB mrb) {
try {
_logger.LogInformation("Attempting to update an MRB");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update MRB set OriginatorID = {mrb.OriginatorID}, ");
queryBuilder.Append($"Title = '{mrb.Title.Replace("'", "''")}', ");
if (mrb.SubmittedDate < DateTimeUtilities.MIN_DT)
mrb.SubmittedDate = DateTimeUtilities.MIN_DT;
queryBuilder.Append($"SubmittedDate = '{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
if (mrb.CloseDate > DateTimeUtilities.MAX_DT)
mrb.CloseDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"CloseDate = '{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
if (mrb.CancelDate > DateTimeUtilities.MAX_DT)
mrb.CancelDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"CancelDate = '{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"NumberOfLotsAffected = {mrb.NumberOfLotsAffected}, ");
if (mrb.ApprovalDate > DateTimeUtilities.MAX_DT)
mrb.ApprovalDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"ApprovalDate = '{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"IssueDescription = '{mrb.IssueDescription.Replace("'", "''")}', ");
queryBuilder.Append($"CustomerImpacted = {Convert.ToInt32(mrb.CustomerImpacted)}, ");
queryBuilder.Append($"Department = '{mrb.Department.Replace("'", "''")}', ");
queryBuilder.Append($"Process = '{mrb.Process.Replace("'", "''")}', ");
queryBuilder.Append($"Val = {mrb.Val}, ");
queryBuilder.Append($"RMANo = {mrb.RMANo}, ");
queryBuilder.Append($"PCRBNo = '{mrb.PCRBNo}', ");
queryBuilder.Append($"SpecsImpacted = {Convert.ToInt32(mrb.SpecsImpacted)}, ");
queryBuilder.Append($"TrainingRequired = {Convert.ToInt32(mrb.TrainingRequired)}, ");
queryBuilder.Append($"Status = '{mrb.Status}', StageNo = {mrb.StageNo}, ");
queryBuilder.Append($"CustomerImpactedName = '{mrb.CustomerImpactedName.Replace("'", "''")}', ");
queryBuilder.Append($"ProcessECNNumber = '{mrb.ProcessECNNumber}', ");
queryBuilder.Append($"Tool = '{mrb.Tool.Replace("'", "''")}', Category = '{mrb.Category.Replace("'", "''")}' ");
queryBuilder.Append($"where MRBNumber = {mrb.MRBNumber};");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception($"Unable to update MRB {mrb.MRBNumber}");
_cache.Set($"mrb{mrb.MRBNumber}", mrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"mrb{mrb.Title}", mrb, DateTimeOffset.Now.AddHours(1));
IEnumerable<MRB>? allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs");
if (allMrbs is not null) {
List<MRB> mrbList = allMrbs.ToList();
mrbList.RemoveAll(m => m.MRBNumber == mrb.MRBNumber);
mrbList.Add(mrb);
_cache.Set("allMrbs", mrbList, DateTimeOffset.Now.AddHours(1));
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to update an MRB. Exception: {ex.Message}");
throw;
}
}
public async Task CreateMRBAction(MRBAction mrbAction) {
try {
_logger.LogInformation("Attempting to generate new MRB action");
if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into MRBAction (Action, Customer, Quantity, PartNumber, LotNumber, MRBNumber, ");
queryBuilder.Append("ConvertFrom, ConvertTo, Justification) ");
queryBuilder.Append($"values ('{mrbAction.Action}', '{mrbAction.Customer}', {mrbAction.Quantity}, ");
queryBuilder.Append($"'{mrbAction.PartNumber}', '{mrbAction.LotNumber}', {mrbAction.MRBNumber}, ");
queryBuilder.Append($"'{mrbAction.ConvertFrom}', '{mrbAction.ConvertTo}', '{mrbAction.Justification}');");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception("Unable to create MRB action in database");
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to create new MRB action. Exception: {ex.Message}");
throw;
}
}
public async Task<IEnumerable<MRBAction>> GetMRBActionsForMRB(int mrbNumber, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get MRB actions for MRB {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
IEnumerable<MRBAction>? mrbActions = null;
if (!bypassCache)
_cache.Get<IEnumerable<MRBAction>>($"mrbActions{mrbNumber}");
if (mrbActions is null) {
string sql = $"select * from MRBAction where MRBNumber = {mrbNumber}";
mrbActions = (await _dalService.QueryAsync<MRBAction>(sql)).ToList();
if (mrbActions is not null) {
foreach (MRBAction action in mrbActions) {
if (action.CompletedDate < DateTime.MaxValue && action.CompletedByUserID > 0) {
action.CompletedByUser = await _userService.GetUserByUserId(action.CompletedByUserID);
}
}
_cache.Set($"mrbActions{mrbNumber}", mrbActions, DateTimeOffset.Now.AddMinutes(30));
}
}
if (mrbActions is null) throw new Exception($"Unable to find MRB actions for MRB {mrbNumber}");
return mrbActions;
} catch (Exception ex) {
_logger.LogError($"Unable to get MRB actions for MRB {mrbNumber}, because {ex.Message}");
throw;
}
}
public async Task UpdateMRBAction(MRBAction mrbAction) {
try {
_logger.LogInformation("Attempting to update MRB action");
if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update MRBAction set Action = '{mrbAction.Action}', ");
queryBuilder.Append($"Customer = '{mrbAction.Customer.Replace("'", "''")}', ");
queryBuilder.Append($"Quantity = {mrbAction.Quantity}, ");
queryBuilder.Append($"PartNumber = '{mrbAction.PartNumber.Replace("'", "''")}', ");
queryBuilder.Append($"LotNumber = '{mrbAction.LotNumber.Replace("'", "''")}', ");
if (mrbAction.AssignedDate < DateTimeUtilities.MIN_DT)
mrbAction.AssignedDate = DateTimeUtilities.MIN_DT;
queryBuilder.Append($"AssignedDate= '{mrbAction.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
if (mrbAction.CompletedDate > DateTimeUtilities.MAX_DT)
mrbAction.CompletedDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"CompletedDate= '{mrbAction.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"CompletedByUserID={mrbAction.CompletedByUserID}, ");
queryBuilder.Append($"ConvertFrom='{mrbAction.ConvertFrom.Replace("'", "''")}', ");
queryBuilder.Append($"ConvertTo='{mrbAction.ConvertTo.Replace("'", "''")}', ");
queryBuilder.Append($"Justification='{mrbAction.Justification.Replace("'", "''")}' ");
queryBuilder.Append($"where ActionID={mrbAction.ActionID};");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception($"There were no MRB actions found with MRB# {mrbAction.MRBNumber}");
List<MRBAction>? mrbActions = _cache.Get<IEnumerable<MRBAction>>($"mrbActions{mrbAction.MRBNumber}")?.ToList();
if (mrbActions is not null) {
mrbActions.RemoveAll(m => m.ActionID == mrbAction.ActionID);
mrbActions.Add(mrbAction);
_cache.Set($"mrbActions{mrbAction.MRBNumber}", mrbActions, DateTimeOffset.Now.AddMinutes(30));
}
} catch (Exception ex) {
_logger.LogError($"Unable to update MRB action, because {ex.Message}");
throw;
}
}
public async Task DeleteMRBAction(int mrbActionID, int mrbNumber) {
try {
_logger.LogInformation($"Attempting to delete MRB action {mrbActionID}");
if (mrbActionID <= 0) throw new ArgumentException($"{mrbActionID} is not a valid MRBActionID");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRBNumber");
string sql = $"delete from MRBAction where ActionID = {mrbActionID};";
int rowsAffected = await _dalService.ExecuteAsync(sql);
if (rowsAffected <= 0) throw new Exception($"No MRB action was found to delete for ActionID {mrbActionID}");
List<MRBAction>? mrbActions = _cache.Get<IEnumerable<MRBAction>>($"mrbActions{mrbNumber}")?.ToList();
if (mrbActions is not null) {
mrbActions.RemoveAll(m => m.ActionID == mrbActionID);
_cache.Set($"mrbActions{mrbNumber}", mrbActions, DateTimeOffset.Now.AddMinutes(30));
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to delete MRB action {mrbActionID}. Exception: {ex.Message}");
throw;
}
}
public async Task<IEnumerable<UploadResult>> UploadAttachments(IEnumerable<IFormFile> files, int mrbNumber) {
try {
_logger.LogInformation("Attempting to upload attachments");
List<UploadResult> uploadResults = new();
if (files is null) throw new ArgumentNullException("Files cannot be null");
if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
foreach (IFormFile file in files) {
try {
if (file is null) throw new ArgumentException("File cannot be null");
if (file.Length <= 0) throw new ArgumentException("File size cannot be zero");
string encodedName = WebUtility.HtmlEncode(file.FileName);
string path = $"{_mrbAttachmentPath}\\{mrbNumber}\\{encodedName}";
await FileUtilities.SaveFileToFileSystem(file, path);
await SaveAttachmentInDb(file, path, mrbNumber);
UploadResult uploadResult = new() {
UploadSuccessful = true,
FileName = file.FileName
};
uploadResults.Add(uploadResult);
} catch (Exception ex) {
UploadResult uploadResult = new() {
UploadSuccessful = false,
FileName = file.FileName,
Error = ex.Message
};
uploadResults.Add(uploadResult);
}
}
return uploadResults;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to upload attachment. Exception: {ex.Message}");
throw;
}
}
public async Task<IEnumerable<UploadResult>> UploadActionAttachments(IEnumerable<IFormFile> files, int actionId) {
try {
_logger.LogInformation("Attempting to upload action attachments");
List<UploadResult> uploadResults = new();
if (files is null) throw new ArgumentNullException("Files cannot be null");
if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty");
if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID");
List<Task> taskList = new();
foreach (IFormFile file in files) {
try {
if (file is null) throw new ArgumentException("File cannot be null");
if (file.Length <= 0) throw new ArgumentException("File size cannot be zero");
string encodedName = WebUtility.HtmlEncode(file.FileName);
string path = $"{_mrbAttachmentPath}\\{actionId}\\{encodedName}";
taskList.Add(FileUtilities.SaveFileToFileSystem(file, path));
taskList.Add(SaveActionAttachmentInDb(file, path, actionId));
UploadResult uploadResult = new() {
UploadSuccessful = true,
FileName = file.Name
};
uploadResults.Add(uploadResult);
} catch (Exception ex) {
UploadResult uploadResult = new() {
UploadSuccessful = false,
FileName = file.Name,
Error = ex.Message
};
uploadResults.Add(uploadResult);
}
}
Task.WaitAll(taskList.ToArray());
return uploadResults;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to upload action attachment. Exception: {ex.Message}");
throw;
}
}
public async Task<IEnumerable<MRBAttachment>> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get all attachments for MRB {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
IEnumerable<MRBAttachment>? attachments = null;
if (!bypassCache)
_cache.Get<IEnumerable<MRBAttachment>>($"mrbAttachments{mrbNumber}");
if (attachments is null) {
string sql = $"select * from MRBAttachment where MRBNumber = {mrbNumber};";
attachments = (await _dalService.QueryAsync<MRBAttachment>(sql)).ToList();
_cache.Set($"mrbAttachments{mrbNumber}", attachments, DateTimeOffset.Now.AddMinutes(15));
}
return attachments;
} catch (Exception ex) {
StringBuilder errMsgBuilder = new();
errMsgBuilder.Append($"An error occurred when attempting to get all attachments for MRB {mrbNumber}. ");
errMsgBuilder.Append($"Exception: {ex.Message}");
_logger.LogError(errMsgBuilder.ToString());
throw;
}
}
public async Task<IEnumerable<MRBActionAttachment>> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get all action attachments for MRB {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#");
List<MRBActionAttachment>? attachments = null;
if (!bypassCache)
attachments = _cache.Get<List<MRBActionAttachment>>($"mrbActionAttachments{mrbNumber}");
if (attachments is null) {
attachments = new List<MRBActionAttachment>();
foreach (MRBAction action in await GetMRBActionsForMRB(mrbNumber, false)) {
string sql = $"select * from MRBActionAttachment where ActionID = {action.ActionID};";
IEnumerable<MRBActionAttachment> newAttachments =
(await _dalService.QueryAsync<MRBActionAttachment>(sql)).ToList();
attachments.AddRange(newAttachments);
}
_cache.Set($"mrbActionAttachments{mrbNumber}", attachments, DateTimeOffset.Now.AddMinutes(15));
}
return attachments;
} catch (Exception ex) {
StringBuilder errMsgBuilder = new();
errMsgBuilder.Append($"An error occurred when attempting to get all attachments for MRB action {mrbNumber}. ");
errMsgBuilder.Append($"Exception: {ex.Message}");
_logger.LogError(errMsgBuilder.ToString());
throw;
}
}
public async Task DeleteAttachment(MRBAttachment attachment) {
try {
_logger.LogInformation("Attempting to delete an attachment");
if (attachment is null) throw new ArgumentNullException("Attachment cannot be null");
if (!File.Exists(attachment.Path)) throw new FileNotFoundException("No file found at provided path");
File.Delete(attachment.Path);
string sql = $"delete from MRBAttachment where AttachmentID = {attachment.AttachmentID};";
int rowsDeleted = await _dalService.ExecuteAsync(sql);
if (rowsDeleted <= 0)
throw new Exception($"No attachments found in the database with attachment ID {attachment.AttachmentID}");
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to delete an attachment. Exception: {ex.Message}");
throw;
}
}
public async Task NotifyNewApprovals(MRB mrb) {
try {
_logger.LogInformation("Attempting to notify approvers");
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(mrb.MRBNumber, true);
List<Approval> approvalsNeedingNotification = approvals.Where(a => a.NotifyDate <= DateTimeUtilities.MIN_DT).ToList();
HashSet<string> emailsAlreadySent = new();
foreach (Approval approval in approvalsNeedingNotification) {
User user = await _userService.GetUserByUserId(approval.UserID);
if (!emailsAlreadySent.Contains(user.Email)) {
emailsAlreadySent.Add(user.Email);
List<MailAddress> toAddresses = new();
toAddresses.Add(new MailAddress(user.Email));
List<MailAddress> ccAddresses = new();
string subject = $"[New Task] Mesa Fab Approval - MRB# {mrb.MRBNumber} - {mrb.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"MRB# {mrb.MRBNumber} [{mrb.Title}] is ready for your approval. ");
bodyBuilder.Append($"The assigned role is {approval.SubRoleCategoryItem}. <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{approval.IssueID} to view the task.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
approval.NotifyDate = DateTime.Now;
await _approvalService.UpdateApproval(approval);
}
}
} catch (Exception ex) {
_logger.LogError($"Unable to notify approvers, because {ex.Message}");
throw;
}
}
public async Task NotifyApprovers(MRBNotification notification) {
try {
_logger.LogInformation("Attempting to send notification to approvers");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(notification.MRB.MRBNumber, true);
HashSet<string> emailsAlreadySent = new();
foreach (Approval approval in approvals) {
User user = await _userService.GetUserByUserId(approval.UserID);
if (!emailsAlreadySent.Contains(user.Email)) {
emailsAlreadySent.Add(user.Email);
List<MailAddress> toAddresses = new();
toAddresses.Add(new MailAddress(user.Email));
List<MailAddress> ccAddresses = new();
string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"{notification.Message} <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{approval.IssueID} to view the MRB.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
approval.NotifyDate = DateTime.Now;
await _approvalService.UpdateApproval(approval);
}
}
} catch (Exception ex) {
_logger.LogError($"Unable to send notification to originator, because {ex.Message}");
throw;
}
}
public async Task NotifyOriginator(MRBNotification notification) {
try {
_logger.LogInformation("Attempting to send notification to originator");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
User user = await _userService.GetUserByUserId(notification.MRB.OriginatorID);
List<MailAddress> toAddresses = new();
toAddresses.Add(new MailAddress(user.Email));
List<MailAddress> ccAddresses = new();
string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"{notification.Message} <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{notification.MRB.MRBNumber} to view the MRB.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
} catch (Exception ex) {
_logger.LogError($"Unable to send notification to originator, because {ex.Message}");
throw;
}
}
public async Task NotifyQAPreApprover(MRBNotification notification) {
try {
_logger.LogInformation("Attempting to send notification to QA pre approver");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
string roleName = "QA_PRE_APPROVAL";
int roleId = await _approvalService.GetRoleIdForRoleName(roleName);
if (roleId <= 0) throw new Exception($"could not find {roleName} role ID");
SubRole? subRole = (await _approvalService.GetSubRolesForSubRoleName(roleName, roleId)).FirstOrDefault();
if (subRole is null)
throw new Exception("Unable to find QA pre approver role");
IEnumerable<User> members = await _approvalService.GetApprovalGroupMembers(subRole.SubRoleID);
List<MailAddress> toAddresses = new();
foreach (User member in members)
toAddresses.Add(new MailAddress(member.Email));
List<MailAddress> ccAddresses = new();
string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"{notification.Message} <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{notification.MRB.MRBNumber} to view the MRB.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
} catch (Exception ex) {
_logger.LogError($"Unable to send notification to QA pre approver, because {ex.Message}");
throw;
}
}
public async Task DeleteMRB(int mrbNumber) {
try {
_logger.LogInformation($"Attempting to delete MRB# {mrbNumber}");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
string sql = $"delete from MRB where MRBNumber={mrbNumber}";
int rowsAffected = await _dalService.ExecuteAsync(sql);
if (rowsAffected <= 0) throw new Exception($"MRB {mrbNumber} not deleted from database");
IEnumerable<MRB>? allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs");
if (allMrbs is not null) {
List<MRB> mrbList = allMrbs.ToList();
mrbList.RemoveAll(m => m.MRBNumber == mrbNumber);
_cache.Set("allMrbs", mrbList, DateTimeOffset.Now.AddHours(1));
}
} catch (Exception ex) {
_logger.LogError($"Unable to delete MRB {mrbNumber}, because {ex.Message}");
throw;
}
}
public async Task ConvertActionsToCsvFile(int mrbNumber, string path) {
try {
_logger.LogInformation($"Attempting to convert MRB {mrbNumber} actions to a CSV file");
if (!(await MRBNumberIsValid(mrbNumber))) throw new ArgumentException($"{mrbNumber} is not a valid ");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (File.Exists(path)) File.Delete(path);
string? directoryPath = Path.GetDirectoryName(path);
if (!string.IsNullOrWhiteSpace(directoryPath))
Directory.CreateDirectory(directoryPath);
IEnumerable<MRBAction> actions = await GetMRBActionsForMRB(mrbNumber, false);
DataTable dt = await ConvertActionsToDataTable(actions);
using StreamWriter sw = new StreamWriter(path, false);
for (int i = 0; i < dt.Columns.Count; i++) {
sw.Write(dt.Columns[i]);
if (i < dt.Columns.Count - 1) sw.Write(",");
}
sw.Write(sw.NewLine);
foreach (DataRow dr in dt.Rows) {
for (int i = 0; i < dt.Columns.Count; i++) {
if (!Convert.IsDBNull(dr[i])) {
string? value = dr[i].ToString();
if (value is null) {
sw.Write("");
} else if (value.Contains(',')) {
value = String.Format("\"{0}\"", value);
sw.Write(value);
} else {
sw.Write(dr[i].ToString());
}
}
if (i < dt.Columns.Count - 1) {
sw.Write(",");
}
}
sw.Write(sw.NewLine);
}
sw.Close();
} catch (Exception ex) {
_logger.LogError($"Unable to convert MRB {mrbNumber} actions to a CSV file, because {ex.Message}");
throw;
}
}
private async Task<DataTable> ConvertActionsToDataTable(IEnumerable<MRBAction> actions) {
try {
_logger.LogInformation("Attempting to convert MRB actions to a DataTable");
if (actions is null) throw new ArgumentNullException("MRB actions cannot be null");
DataTable dt = new();
if (actions.Count() > 0 && actions.First().Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) {
dt.Columns.Add("Action", typeof(string));
dt.Columns.Add("From Customer", typeof(string));
dt.Columns.Add("From Part Number", typeof(string));
dt.Columns.Add("Batch Number / Lot Number", typeof(string));
dt.Columns.Add("Qty", typeof(string));
dt.Columns.Add("To Customer", typeof(string));
dt.Columns.Add("To Part Number", typeof(string));
dt.Columns.Add("Assigned Date", typeof(string));
dt.Columns.Add("Completed Date", typeof(string));
dt.Columns.Add("Completed By", typeof(string));
foreach (MRBAction action in actions) {
if (action.CompletedByUser is null && action.CompletedByUserID > 0)
action.CompletedByUser = await _userService.GetUserByUserId(action.CompletedByUserID);
string convertFromCustomer = string.Empty;
string convertFromPart = string.Empty;
string convertToCustomer = string.Empty;
string convertToPart = string.Empty;
string[] convertFrom = action.ConvertFrom.Split(" ");
if (convertFrom.Length > 1) {
convertFromCustomer = convertFrom[0];
foreach (string partStr in convertFrom.Skip(1))
convertFromPart += partStr;
}
string[] convertTo = action.ConvertTo.Split(" ");
if (convertTo.Length > 1) {
convertToCustomer = convertTo[0];
foreach (string partStr in convertTo.Skip(1))
convertToPart += partStr;
}
dt.Rows.Add(action.Action, convertFromCustomer, convertFromPart, action.Quantity.ToString(),
convertToCustomer, convertToPart,
DateTimeUtilities.GetDateAsStringMinDefault(action.AssignedDate),
DateTimeUtilities.GetDateAsStringMaxDefault(action.CompletedDate),
action.CompletedByUser is null ? "" : action.CompletedByUser.GetFullName());
}
} else {
dt.Columns.Add("Action", typeof(string));
dt.Columns.Add("Customer", typeof(string));
dt.Columns.Add("Qty", typeof(string));
dt.Columns.Add("Convert From", typeof(string));
dt.Columns.Add("Convert To", typeof(string));
dt.Columns.Add("Part Number", typeof(string));
dt.Columns.Add("Batch Number / Lot Number", typeof(string));
dt.Columns.Add("Justification", typeof(string));
dt.Columns.Add("Assigned Date", typeof(string));
dt.Columns.Add("Completed Date", typeof(string));
dt.Columns.Add("Completed By", typeof(string));
foreach (MRBAction action in actions) {
if (action.CompletedByUser is null && action.CompletedByUserID > 0)
action.CompletedByUser = await _userService.GetUserByUserId(action.CompletedByUserID);
dt.Rows.Add(action.Action, action.Customer, action.Quantity.ToString(), action.ConvertFrom, action.ConvertTo,
action.PartNumber, action.LotNumber, action.Justification,
DateTimeUtilities.GetDateAsStringMinDefault(action.AssignedDate),
DateTimeUtilities.GetDateAsStringMaxDefault(action.CompletedDate),
action.CompletedByUser is null ? "" : action.CompletedByUser.GetFullName());
}
}
return dt;
} catch (Exception ex) {
_logger.LogError($"Unable to convert MRB actions to a DataTable, because {ex.Message}");
throw;
}
}
private async Task SaveAttachmentInDb(IFormFile file, string path, int mrbNumber) {
try {
_logger.LogInformation($"Attempting to save attachment to database");
if (file is null) throw new ArgumentNullException("File cannot be null");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into MRBAttachment (MRBNumber, FileName, UploadDate, Path) ");
queryBuilder.Append($"values ({mrbNumber}, '{file.FileName}', ");
queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', '{path}');");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0)
throw new Exception("Unable to insert attachment in database");
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to save file to DB. Exception: {ex.Message}");
throw;
}
}
private async Task SaveActionAttachmentInDb(IFormFile file, string path, int actionId) {
try {
_logger.LogInformation($"Attempting to save action attachment to database");
if (file is null) throw new ArgumentNullException("File cannot be null");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into MRBActionAttachment (ActionID, FileName, UploadDate, Path) ");
queryBuilder.Append($"values ({actionId}, '{file.FileName}', ");
queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', '{path}');");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0)
throw new Exception("Unable to insert action attachment in database");
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to save file to DB. Exception: {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,767 @@
using System.Net;
using System.Net.Mail;
using System.Text;
using MesaFabApproval.API.Utilities;
using MesaFabApproval.Models;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Utilities;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface IPCRBService {
Task CreateNewPCRB(PCRB pcrb);
Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache);
Task<PCRB> GetPCRBByPlanNumber(int planNumber, bool bypassCache);
Task<PCRB> GetPCRBByTitle(string title, bool bypassCache);
Task UpdatePCRB(PCRB pcrb);
Task DeletePCRB(int planNumber);
Task<UploadResult> UploadAttachment(IFormFile file, PCRBAttachment attachment);
Task<IEnumerable<PCRBAttachment>> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache);
Task UpdateAttachment(PCRBAttachment attachment);
Task DeleteAttachment(PCRBAttachment attachment);
Task CreateNewActionItem(PCRBActionItem actionItem);
Task UpdateActionItem(PCRBActionItem actionItem);
Task DeleteActionItem(int id);
Task<IEnumerable<PCRBActionItem>> GetActionItemsForPlanNumber(int planNumber, bool bypassCache);
Task CreateNewAttendee(PCRBAttendee attendee);
Task UpdateAttendee(PCRBAttendee attendee);
Task DeleteAttendee(int id);
Task<IEnumerable<PCRBAttendee>> GetAttendeesByPlanNumber(int planNumber, bool bypassCache);
Task CreatePCR3Document(PCR3Document document);
Task UpdatePCR3Document(PCR3Document document);
Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache);
Task NotifyNewApprovals(PCRB pcrb);
Task NotifyApprovers(PCRBNotification notification);
Task NotifyOriginator(PCRBNotification notification);
Task NotifyResponsiblePerson(PCRBActionItemNotification notification);
}
public class PCRBService : IPCRBService {
private readonly ILogger<PCRBService> _logger;
private readonly IDalService _dalService;
private readonly IMemoryCache _cache;
private readonly IUserService _userService;
private readonly IApprovalService _approvalService;
private readonly ISmtpService _smtpService;
private readonly string _pcrbAttachmentPath;
private readonly string _siteBaseUrl;
public PCRBService(ILogger<PCRBService> logger,
IDalService dalService,
IMemoryCache cache,
IUserService userService,
IApprovalService approvalService,
ISmtpService smtpService,
AppSettings appSettings) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_dalService = dalService ?? throw new ArgumentNullException("IDalService not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
_approvalService = approvalService ??
throw new ArgumentNullException("IApprovalService not injected");
_smtpService = smtpService ?? throw new ArgumentNullException("ISmtpService not injected");
_siteBaseUrl = appSettings.SiteBaseUrl;
_pcrbAttachmentPath = appSettings.PcrbAttachmentPath;
}
public async Task CreateNewPCRB(PCRB pcrb) {
try {
_logger.LogInformation("Attempting to create new PCRB");
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into CCChangeControl (OwnerID, Title, ChangeLevel, ReasonForChange, ");
queryBuilder.Append("ChangeDescription, IsITAR, CurrentStep, InsertTimeStamp, LastUpdateDate) ");
queryBuilder.Append($"values ({pcrb.OwnerID}, '{pcrb.Title}', '{pcrb.ChangeLevel}', ");
queryBuilder.Append($"'{pcrb.ReasonForChange}', '{pcrb.ChangeDescription}', ");
queryBuilder.Append($"{Convert.ToInt32(pcrb.IsITAR)}, {pcrb.CurrentStep}, ");
queryBuilder.Append($"'{pcrb.InsertTimeStamp.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}')");
int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsCreated <= 0) throw new Exception("unable to insert new PCRB in the database");
} catch (Exception ex) {
_logger.LogError($"Unable to create new PCRB, because {ex.Message}");
throw;
}
}
public async Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache) {
try {
_logger.LogInformation("Attempting to get all PCRBs");
IEnumerable<PCRB>? allPCRBs = null;
if (!bypassCache) allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs");
if (allPCRBs is null) {
string sql = "select * from CCChangeControl";
allPCRBs = (await _dalService.QueryAsync<PCRB>(sql)).ToList();
foreach (PCRB pcrb in allPCRBs) {
if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0)
pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName();
}
_cache.Set("allPCRBs", allPCRBs, DateTimeOffset.Now.AddHours(1));
}
if (allPCRBs is null || allPCRBs.Count() == 0)
throw new Exception("no PCRBs found");
return allPCRBs;
} catch (Exception ex) {
_logger.LogError($"Unable to get all PCRBs, because {ex.Message}");
throw;
}
}
public async Task<PCRB> GetPCRBByPlanNumber(int planNumber, bool bypassCache) {
try {
_logger.LogInformation("Attempting to get a PCRB by plan#");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB#");
PCRB? pcrb = null;
if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{planNumber}");
if (pcrb is null) {
string sql = $"select * from CCChangeControl where PlanNumber={planNumber}";
pcrb = (await _dalService.QueryAsync<PCRB>(sql)).FirstOrDefault();
if (pcrb is not null) {
if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0)
pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName();
_cache.Set($"pcrb{planNumber}", pcrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1));
}
}
if (pcrb is null) throw new Exception($"unable to find PCRB {planNumber}");
return pcrb;
} catch (Exception ex) {
_logger.LogError($"Unable to get PCRB by Plan #, because {ex.Message}");
throw;
}
}
public async Task<PCRB> GetPCRBByTitle(string title, bool bypassCache) {
try {
_logger.LogInformation("Attempting to get a PCRB by title");
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty");
PCRB? pcrb = null;
if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{title}");
if (pcrb is null) {
string sql = $"select * from CCChangeControl where Title='{title}'";
pcrb = (await _dalService.QueryAsync<PCRB>(sql)).FirstOrDefault();
if (pcrb is not null) {
if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0)
pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName();
_cache.Set($"pcrb{title}", pcrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1));
}
}
if (pcrb is null) throw new Exception($"unable to find PCRB {title}");
return pcrb;
} catch (Exception ex) {
_logger.LogError($"Unable to get PCRB by title, because {ex.Message}");
throw;
}
}
public async Task UpdatePCRB(PCRB pcrb) {
try {
_logger.LogInformation($"Attempting to update PCRB");
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update CCChangeControl set OwnerID={pcrb.OwnerID}, ");
queryBuilder.Append($"Title='{pcrb.Title.Replace("'", "''")}', ChangeLevel='{pcrb.ChangeLevel}', ");
queryBuilder.Append($"CurrentStep={pcrb.CurrentStep}, ReasonForChange='{pcrb.ReasonForChange.Replace("'", "''")}', ");
queryBuilder.Append($"ChangeDescription='{pcrb.ChangeDescription.Replace("'", "''")}', ");
queryBuilder.Append($"IsITAR={Convert.ToInt32(pcrb.IsITAR)}, ");
queryBuilder.Append($"InsertTimeStamp='{pcrb.InsertTimeStamp.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"ClosedDate='{pcrb.ClosedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"LastUpdateDate='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}' ");
queryBuilder.Append($"where PlanNumber={pcrb.PlanNumber}");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception("unable to perform update in database");
_cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1));
IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs");
if (allPCRBs is not null) {
List<PCRB> pcrbList = allPCRBs.ToList();
pcrbList.RemoveAll(p => p.PlanNumber == pcrb.PlanNumber);
pcrbList.Add(pcrb);
_cache.Set("allPCRBs", pcrbList, DateTimeOffset.Now.AddHours(1));
}
} catch (Exception ex) {
_logger.LogError($"Unable to update PCRB, because {ex.Message}");
throw;
}
}
public async Task DeletePCRB(int planNumber) {
try {
_logger.LogInformation($"Attempting to delete PCRB {planNumber}");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan #");
string sql = $"delete from CCChangeControl where PlanNumber={planNumber}";
int rowsAffected = await _dalService.ExecuteAsync(sql);
if (rowsAffected <= 0) throw new Exception("delete operation failed in database");
IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs");
if (allPCRBs is not null) {
List<PCRB> pcrbList = allPCRBs.ToList();
pcrbList.RemoveAll(p => p.PlanNumber == planNumber);
_cache.Set("allPCRBs", pcrbList, DateTimeOffset.Now.AddHours(1));
}
} catch (Exception ex) {
_logger.LogError($"Unable to delete PCRB {planNumber}, because {ex.Message}");
throw;
}
}
public async Task<UploadResult> UploadAttachment(IFormFile file, PCRBAttachment attachment) {
try {
_logger.LogInformation("Attempting to upload attachment");
UploadResult? uploadResult = null;
if (file is null) throw new ArgumentNullException("File cannot be null");
if (file.Length <= 0) throw new ArgumentException("File size cannot be zero");
if (attachment is null) throw new ArgumentNullException("Attachment cannot be null");
try {
string encodedName = WebUtility.HtmlEncode(file.FileName);
string path = $"{_pcrbAttachmentPath}\\{attachment.PlanNumber}\\{attachment.Step}\\{encodedName}";
attachment.Path = path;
await FileUtilities.SaveFileToFileSystem(file, path);
await SaveAttachmentInDb(file, attachment);
uploadResult = new() {
UploadSuccessful = true,
FileName = file.FileName
};
} catch (Exception ex) {
uploadResult = new() {
UploadSuccessful = false,
FileName = file.FileName,
Error = ex.Message
};
}
return uploadResult;
} catch (Exception ex) {
_logger.LogError($"Unable to upload attachment, because {ex.Message}");
throw;
}
}
public async Task<IEnumerable<PCRBAttachment>> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get all attachments for PCRB Plan# {planNumber}");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
IEnumerable<PCRBAttachment>? attachments = null;
if (!bypassCache)
attachments = _cache.Get<IEnumerable<PCRBAttachment>>($"pcrbAttachments{planNumber}");
if (attachments is null) {
string sql = $"select * from CCAttachment where PlanNumber={planNumber}";
attachments = await _dalService.QueryAsync<PCRBAttachment>(sql);
_cache.Set($"pcrbAttachments{planNumber}", attachments, DateTimeOffset.Now.AddMinutes(15));
}
return attachments;
} catch (Exception ex) {
_logger.LogError($"Unable to get all attachments for PCRB Plan# {planNumber}, because {ex.Message}");
throw;
}
}
public async Task UpdateAttachment(PCRBAttachment attachment) {
try {
_logger.LogInformation("Attempting to update an attachment");
if (attachment is null)
throw new ArgumentNullException("attachment cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update CCAttachment ");
queryBuilder.Append($"set Title='{attachment.Title.Replace("'", "''")}' where ID={attachment.ID}");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception("update failed in database");
} catch (Exception ex) {
_logger.LogError($"Unable to update attachment, because {ex.Message}");
throw;
}
}
public async Task DeleteAttachment(PCRBAttachment attachment) {
try {
_logger.LogInformation("Attempting to update an attachment");
if (attachment is null)
throw new ArgumentNullException("attachment cannot be null");
if (!File.Exists(attachment.Path)) throw new FileNotFoundException("No file found at provided path");
File.Delete(attachment.Path);
string sql = $"delete from CCAttachment where ID={attachment.ID}";
int rowsAffected = await _dalService.ExecuteAsync(sql);
if (rowsAffected <= 0) throw new Exception("update failed in database");
} catch (Exception ex) {
_logger.LogError($"Unable to update attachment, because {ex.Message}");
throw;
}
}
public async Task CreateNewActionItem(PCRBActionItem actionItem) {
try {
_logger.LogInformation("Attempting to create new action item");
if (actionItem is null) throw new ArgumentNullException("action item cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into CCPCRBActionItem (Name, Gating, ClosedStatus, ClosedDate, ");
queryBuilder.Append("ClosedByID, UploadedByID, UploadedDateTime, ResponsiblePersonID, PlanNumber, ");
queryBuilder.Append($"Step) values ('{actionItem.Name}', {Convert.ToInt32(actionItem.Gating)}, ");
queryBuilder.Append($"{Convert.ToInt32(actionItem.ClosedStatus)}, ");
DateTime closedDateCopy = actionItem.ClosedDate ?? DateTimeUtilities.MAX_DT;
queryBuilder.Append($"'{closedDateCopy.ToString("yyyy-MM-dd HH:mm:ss")}', {actionItem.ClosedByID}, ");
queryBuilder.Append($"{actionItem.UploadedByID}, '{actionItem.UploadedDateTime.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"{actionItem.ResponsiblePersonID}, {actionItem.PlanNumber}, ");
queryBuilder.Append($"{actionItem.Step});");
int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsCreated <= 0) throw new Exception("unable to insert new action item in the database");
} catch (Exception ex) {
_logger.LogError($"Unable to create new action item, because {ex.Message}");
throw;
}
}
public async Task UpdateActionItem(PCRBActionItem actionItem) {
try {
_logger.LogInformation("Attempting to update an action item");
if (actionItem is null)
throw new ArgumentNullException("action item cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update CCPCRBActionItem set Name='{actionItem.Name.Replace("'", "''")}', Gating={Convert.ToInt32(actionItem.Gating)}, ");
queryBuilder.Append($"ClosedStatus={Convert.ToInt32(actionItem.ClosedStatus)}, ");
DateTime closedDateCopy = actionItem.ClosedDate ?? DateTimeUtilities.MAX_DT;
queryBuilder.Append($"ClosedDate='{closedDateCopy.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"ClosedByID={actionItem.ClosedByID}, ResponsiblePersonID={actionItem.ResponsiblePersonID}, ");
queryBuilder.Append($"Step={actionItem.Step} where ID={actionItem.ID}");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception("update failed in database");
} catch (Exception ex) {
_logger.LogError($"Unable to update attachment, because {ex.Message}");
throw;
}
}
public async Task DeleteActionItem(int id) {
try {
_logger.LogInformation($"Attempting to delete action item {id}");
if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB action item ID");
string sql = $"delete from CCPCRBActionItem where ID={id}";
int rowsAffected = await _dalService.ExecuteAsync(sql);
if (rowsAffected <= 0) throw new Exception("delete operation failed in database");
} catch (Exception ex) {
_logger.LogError($"Unable to delete action item {id}, because {ex.Message}");
throw;
}
}
public async Task<IEnumerable<PCRBActionItem>> GetActionItemsForPlanNumber(int planNumber, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get all action items for PCRB plan# {planNumber}");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan#");
IEnumerable<PCRBActionItem>? actionItems = null;
if (!bypassCache)
actionItems = _cache.Get<IEnumerable<PCRBActionItem>>($"pcrbActionItems{planNumber}");
if (actionItems is null) {
string sql = $"select * from CCPCRBActionItem where PlanNumber={planNumber}";
actionItems = await _dalService.QueryAsync<PCRBActionItem>(sql);
_cache.Set($"pcrbActionItems{planNumber}", actionItems, DateTimeOffset.Now.AddMinutes(15));
}
return actionItems;
} catch (Exception ex) {
_logger.LogError($"Unable to get all action items for PCRB plan# {planNumber}, because {ex.Message}");
throw;
}
}
public async Task CreateNewAttendee(PCRBAttendee attendee) {
try {
_logger.LogInformation("Attempting to create new attendee");
if (attendee is null) throw new ArgumentNullException("attendee item cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into CCPCRBAttendee (PlanNumber, JobTitle, Location, Attended, AttendeeID, Step) ");
queryBuilder.Append($"values ({attendee.PlanNumber}, '{attendee.JobTitle}', '{attendee.Location}', ");
queryBuilder.Append($"{Convert.ToInt32(attendee.Attended)}, {attendee.AttendeeID}, ");
queryBuilder.Append($"{attendee.Step});");
int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsCreated <= 0) throw new Exception("unable to insert new attendee in the database");
} catch (Exception ex) {
_logger.LogError($"Unable to create new attendee, because {ex.Message}");
throw;
}
}
public async Task UpdateAttendee(PCRBAttendee attendee) {
try {
_logger.LogInformation("Attempting to update an attendee");
if (attendee is null)
throw new ArgumentNullException("attendee cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update CCPCRBAttendee set JobTitle='{attendee.JobTitle}', ");
queryBuilder.Append($"Location='{attendee.Location}', Attended={Convert.ToInt32(attendee.Attended)}, ");
queryBuilder.Append($"AttendeeID={attendee.AttendeeID}, ");
queryBuilder.Append($"Step={attendee.Step} where ID={attendee.ID}");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception("update failed in database");
} catch (Exception ex) {
_logger.LogError($"Unable to update attendee, because {ex.Message}");
throw;
}
}
public async Task DeleteAttendee(int id) {
try {
_logger.LogInformation($"Attempting to delete attendee {id}");
if (id <= 0) throw new ArgumentException($"{id} is not a valid attendee ID");
string sql = $"delete from CCPCRBAttendee where ID={id}";
int rowsAffected = await _dalService.ExecuteAsync(sql);
if (rowsAffected <= 0) throw new Exception("delete operation failed in database");
} catch (Exception ex) {
_logger.LogError($"Unable to delete attendee {id}, because {ex.Message}");
throw;
}
}
public async Task<IEnumerable<PCRBAttendee>> GetAttendeesByPlanNumber(int planNumber, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get all attendees for PCRB plan# {planNumber}");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan#");
IEnumerable<PCRBAttendee>? attendees = null;
if (!bypassCache)
attendees = _cache.Get<IEnumerable<PCRBAttendee>>($"pcrbAttendees{planNumber}");
if (attendees is null) {
string sql = $"select * from CCPCRBAttendee where PlanNumber={planNumber}";
attendees = await _dalService.QueryAsync<PCRBAttendee>(sql);
_cache.Set($"pcrbAttendees{planNumber}", attendees, DateTimeOffset.Now.AddMinutes(15));
}
return attendees;
} catch (Exception ex) {
_logger.LogError($"Unable to get all attendees for PCRB plan# {planNumber}, because {ex.Message}");
throw;
}
}
public async Task CreatePCR3Document(PCR3Document document) {
try {
_logger.LogInformation("Attempting to create new PCR3 document");
if (document is null) throw new ArgumentNullException("document item cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into CCPCR3Document (PlanNumber, DocType) ");
queryBuilder.Append($"values ({document.PlanNumber}, '{document.DocType}')");
int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsCreated <= 0) throw new Exception("unable to insert new PCR3 document in the database");
} catch (Exception ex) {
_logger.LogError($"Unable to create new PCR3 document, because {ex.Message}");
throw;
}
}
public async Task UpdatePCR3Document(PCR3Document document) {
try {
_logger.LogInformation("Attempting to update a PCR3 document");
if (document is null) throw new ArgumentNullException("document cannot be null");
StringBuilder queryBuilder = new();
queryBuilder.Append($"update CCPCR3Document set DocNumbers='{document.DocNumbers}', ");
queryBuilder.Append($"Comment='{document.Comment.Replace("'", "''")}', ECNNumber={document.ECNNumber}, ");
queryBuilder.Append($"CompletedDate='{document.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"CompletedByID={document.CompletedByID} ");
queryBuilder.Append($"where ID={document.ID}");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0) throw new Exception("update failed in database");
} catch (Exception ex) {
_logger.LogError($"Unable to update PCR3 document, because {ex.Message}");
throw;
}
}
public async Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache) {
try {
_logger.LogInformation($"Attempting to get all PCR3 documents for PCRB plan# {planNumber}");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan#");
IEnumerable<PCR3Document>? documents = null;
if (!bypassCache)
documents = _cache.Get<IEnumerable<PCR3Document>>($"pcr3Documents{planNumber}");
if (documents is null) {
string sql = $"select * from CCPCR3Document where PlanNumber={planNumber}";
documents = await _dalService.QueryAsync<PCR3Document>(sql);
_cache.Set($"pcr3Documents{planNumber}", documents, DateTimeOffset.Now.AddMinutes(15));
}
return documents;
} catch (Exception ex) {
_logger.LogError($"Unable to get all PCR3 documents for PCRB plan# {planNumber}, because {ex.Message}");
throw;
}
}
public async Task NotifyNewApprovals(PCRB pcrb) {
try {
_logger.LogInformation("Attempting to notify approvers");
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(pcrb.PlanNumber, true);
List<Approval> approvalsNeedingNotification = approvals.Where(a => a.Step == pcrb.CurrentStep &&
a.NotifyDate <= DateTimeUtilities.MIN_DT &&
a.AssignedDate > DateTimeUtilities.MIN_DT).ToList();
HashSet<string> emailsAlreadySent = new();
foreach (Approval approval in approvalsNeedingNotification) {
User user = await _userService.GetUserByUserId(approval.UserID);
if (!emailsAlreadySent.Contains(user.Email.ToLower())) {
emailsAlreadySent.Add(user.Email);
List<MailAddress> toAddresses = new();
toAddresses.Add(new MailAddress(user.Email.ToLower()));
List<MailAddress> ccAddresses = new();
string subject = $"[New Task] Mesa Fab Approval - PCRB# {pcrb.PlanNumber} - {pcrb.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"PCRB# {pcrb.PlanNumber} [{pcrb.Title}] PCR{approval.Step} is ready for your approval. ");
bodyBuilder.Append($"The assigned role is {approval.SubRoleCategoryItem}. <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{approval.IssueID} to view the PCRB.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
approval.NotifyDate = DateTime.Now;
await _approvalService.UpdateApproval(approval);
}
}
} catch (Exception ex) {
_logger.LogError($"Unable to notify approvers, because {ex.Message}");
throw;
}
}
public async Task NotifyApprovers(PCRBNotification notification) {
try {
_logger.LogInformation("Attempting to send notification to approvers");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(notification.PCRB.PlanNumber, true);
HashSet<string> emailsAlreadySent = new();
foreach (Approval approval in approvals) {
User user = await _userService.GetUserByUserId(approval.UserID);
if (!emailsAlreadySent.Contains(user.Email)) {
emailsAlreadySent.Add(user.Email);
List<MailAddress> toAddresses = new();
toAddresses.Add(new MailAddress(user.Email));
List<MailAddress> ccAddresses = new();
string subject = $"[Update] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - {notification.PCRB.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"{notification.Message} <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{approval.IssueID} to view the PCRB.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
approval.NotifyDate = DateTime.Now;
await _approvalService.UpdateApproval(approval);
}
}
} catch (Exception ex) {
_logger.LogError($"Unable to send notification to originator, because {ex.Message}");
throw;
}
}
public async Task NotifyOriginator(PCRBNotification notification) {
try {
_logger.LogInformation("Attempting to send notification to originator");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
User user = await _userService.GetUserByUserId(notification.PCRB.OwnerID);
List<MailAddress> toAddresses = new();
toAddresses.Add(new MailAddress(user.Email));
List<MailAddress> ccAddresses = new();
string subject = $"[Update] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - {notification.PCRB.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"{notification.Message} <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{notification.PCRB.PlanNumber} to view the PCRB.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
} catch (Exception ex) {
_logger.LogError($"Unable to send notification to originator, because {ex.Message}");
throw;
}
}
public async Task NotifyResponsiblePerson(PCRBActionItemNotification notification) {
try {
_logger.LogInformation("Attempting to notify responsible person");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message))
throw new ArgumentException("message cannot be null or empty");
if (notification.ActionItem.ResponsiblePerson is null)
notification.ActionItem.ResponsiblePerson = await _userService.GetUserByUserId(notification.ActionItem.ResponsiblePersonID);
List<MailAddress> toAddresses = new();
toAddresses.Add(new MailAddress(notification.ActionItem.ResponsiblePerson.Email));
List<MailAddress> ccAddresses = new();
string subject = $"[New Task] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - {notification.PCRB.Title}";
StringBuilder bodyBuilder = new();
bodyBuilder.Append($"{notification.Message} <br /> <br />");
bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{notification.PCRB.PlanNumber} to view the PCRB.");
await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString());
} catch (Exception ex) {
_logger.LogError($"Unable to notify responsible person, because {ex.Message}");
throw;
}
}
private async Task SaveAttachmentInDb(IFormFile file, PCRBAttachment attachment) {
try {
_logger.LogInformation($"Attempting to save attachment to database");
if (file is null) throw new ArgumentNullException("File cannot be null");
if (string.IsNullOrWhiteSpace(attachment.Path)) throw new ArgumentException("Path cannot be null or empty");
if (attachment.PlanNumber <= 0) throw new ArgumentException($"{attachment.PlanNumber} is not a valid PCRB Plan#");
StringBuilder queryBuilder = new();
queryBuilder.Append("insert into CCAttachment (PlanNumber, FileName, UploadDateTime, Path, UploadedByID, Title, ");
queryBuilder.Append($"Step) values ({attachment.PlanNumber}, '{file.FileName}', ");
queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', '{attachment.Path}', {attachment.UploadedByID}, ");
queryBuilder.Append($"'{attachment.Title}', {attachment.Step});");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
if (rowsAffected <= 0)
throw new Exception("Unable to insert attachment in database");
} catch (Exception ex) {
_logger.LogError($"Unable to save file to DB, because {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,79 @@
using System.Net.Mail;
using MesaFabApproval.API.Clients;
using MesaFabApproval.Models;
using Microsoft.IdentityModel.Tokens;
namespace MesaFabApproval.API.Services;
public interface ISmtpService {
Task<bool> SendEmail(IEnumerable<MailAddress> recipients, IEnumerable<MailAddress> ccRecipients, string subject, string body);
}
public class SmtpService : ISmtpService {
private readonly ILogger<SmtpService> _logger;
private readonly ISmtpClientWrapper _smtpClient;
private readonly bool _shouldSendEmail;
public SmtpService(ILogger<SmtpService> logger, ISmtpClientWrapper smtpClient, AppSettings appSettings) {
_logger = logger ??
throw new ArgumentNullException("ILogger not injected");
_smtpClient = smtpClient ??
throw new ArgumentNullException("SmtpClient not injected");
_shouldSendEmail = appSettings.ShouldSendEmail;
}
public async Task<bool> SendEmail(IEnumerable<MailAddress> recipients,
IEnumerable<MailAddress> ccRecipients,
string subject,
string body) {
if (recipients.IsNullOrEmpty()) throw new ArgumentNullException("recipients cannot be null or empty!");
if (ccRecipients is null) throw new ArgumentNullException("ccRecipients cannot be null!");
if (subject.IsNullOrEmpty()) throw new ArgumentNullException("subject cannot be null or empty!");
if (body.IsNullOrEmpty()) throw new ArgumentNullException("body cannot be null or empty!");
return await Task.Run(() => {
int maxRetries = 3;
int backoffSeconds = 30;
bool messageWasSent = false;
try {
if (_shouldSendEmail) {
int remainingRetries = maxRetries;
while (!messageWasSent && remainingRetries > 0) {
try {
Task.Delay((maxRetries - remainingRetries--) * backoffSeconds * 1000);
_logger.LogInformation($"Attempting to send notification. Remaining retries: {remainingRetries}");
MailMessage msg = new MailMessage();
msg.IsBodyHtml = true;
msg.From = new MailAddress("MesaFabApproval@infineon.com", "Mesa Fab Approval");
msg.Sender = new MailAddress("MesaFabApproval@infineon.com", "Mesa Fab Approval");
foreach (MailAddress recipient in recipients) msg.To.Add(recipient);
msg.Bcc.Add("chase.tucker@infineon.com");
foreach (MailAddress ccRecipient in ccRecipients) msg.CC.Add(ccRecipient);
msg.Subject = subject;
msg.Body = body;
_smtpClient.Send(msg);
messageWasSent = true;
} catch (Exception ex) {
_logger.LogError($"Message not sent successfully. Exception: {ex.Message}");
}
}
} else {
_logger.LogInformation("Not sending email per local configuration");
messageWasSent = true;
}
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to send notification. Exception: {ex.Message}");
}
return messageWasSent;
});
}
}

View File

@ -0,0 +1,147 @@
using System.Text;
using MesaFabApproval.Shared.Models;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.API.Services;
public interface IUserService {
Task<IEnumerable<User>> GetAllActiveUsers();
Task<User> GetUserByLoginId(string loginId);
Task<User> GetUserByUserId(int userId);
Task<IEnumerable<int>> GetApproverUserIdsBySubRoleCategoryItem(string item);
}
public class UserService : IUserService {
private readonly ILogger<UserService> _logger;
private readonly IDalService _dalService;
private readonly IMemoryCache _cache;
public UserService(ILogger<UserService> logger, IDalService dalService, IMemoryCache cache) {
_logger = logger ??
throw new ArgumentNullException("ILogger not injected");
_dalService = dalService ??
throw new ArgumentNullException("IDalService not injected");
_cache = cache ??
throw new ArgumentNullException("IMemoryCache not injected");
}
public async Task<IEnumerable<User>> GetAllActiveUsers() {
try {
_logger.LogInformation("Attempting to get all active users");
IEnumerable<User>? allActiveUsers = _cache.Get<IEnumerable<User>>("allActiveUsers");
if (allActiveUsers is null) {
string sql = "select * from Users where IsActive = 1";
allActiveUsers = (await _dalService.QueryAsync<User>(sql)).ToList();
_cache.Set("allActiveUsers", allActiveUsers, DateTimeOffset.Now.AddHours(1));
}
if (allActiveUsers is null || allActiveUsers.Count() == 0) {
throw new Exception("No users found");
}
return allActiveUsers;
} catch (Exception ex) {
string errMsg = $"An exception occurred when attempting to get all users. Exception: {ex.Message}";
_logger.LogError(errMsg);
throw;
}
}
public async Task<User> GetUserByLoginId(string loginId) {
try {
_logger.LogInformation("Attempting to get user by LoginId");
if (string.IsNullOrWhiteSpace(loginId))
throw new ArgumentException("LoginId cannot be null or empty");
User? user = _cache.Get<User>($"userByLoginId{loginId}");
if (user is null)
user = _cache.Get<IEnumerable<User>>("allActiveUsers")?.FirstOrDefault(u => u.LoginID == loginId);
if (user is null) {
string sql = $"select * from Users where LoginID = '{loginId}';";
user = (await _dalService.QueryAsync<User>(sql)).FirstOrDefault();
_cache.Set($"userByLoginId{loginId}", user, DateTimeOffset.Now.AddHours(1));
}
if (user is null) throw new Exception($"No user found with LoginID {loginId}");
return user;
} catch (Exception ex) {
string errMsg = $"An exception occurred when attempting to get user for LoginID {loginId}. Exception: {ex.Message}";
_logger.LogError(errMsg);
throw;
}
}
public async Task<User> GetUserByUserId(int userId) {
try {
_logger.LogInformation("Attempting to get user by user ID");
if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID");
User? user = _cache.Get<User>($"userByUserId{userId}");
if (user is null)
user = _cache.Get<IEnumerable<User>>("allActiveUsers")?.FirstOrDefault(u => u.UserID == userId);
if (user is null) {
string sql = $"select * from Users where UserID = '{userId}';";
user = (await _dalService.QueryAsync<User>(sql)).FirstOrDefault();
_cache.Set($"userByUserId{userId}", user, DateTimeOffset.Now.AddHours(1));
}
if (user is null) throw new Exception($"No user found with UserID {userId}");
return user;
} catch (Exception ex) {
string errMsg = $"An exception occurred when attempting to get user for UserID {userId}. Exception: {ex.Message}";
_logger.LogError(errMsg);
throw;
}
}
public async Task<IEnumerable<int>> GetApproverUserIdsBySubRoleCategoryItem(string item) {
try {
_logger.LogInformation("Attempting to get approver user IDs");
if (string.IsNullOrWhiteSpace(item)) throw new ArgumentException("SubRoleCategoryItem cannot be null or empty");
IEnumerable<int>? userIds = _cache.Get<IEnumerable<int>>($"approverUserIdsBySubRollCategory{item}");
if (userIds is null) {
StringBuilder queryBuilder = new();
queryBuilder.Append("select us.UserID ");
queryBuilder.Append("from SubRole as sr ");
queryBuilder.Append("join UserSubRole as us on sr.SubRoleID=us.SubRoleID ");
queryBuilder.Append("join SubRoleCategory as sc on sr.SubRoleCategoryID=sc.SubRoleCategoryID ");
queryBuilder.Append($"where sc.SubRoleCategoryItem='{item}'");
userIds = (await _dalService.QueryAsync<int>(queryBuilder.ToString())).ToList();
_cache.Set($"approverUserIdsBySubRollCategory{item}", userIds, DateTimeOffset.Now.AddHours(1));
}
if (userIds is null || userIds.Count() == 0) {
throw new Exception($"No users found for SubRoleCategoryItem {item}");
}
return userIds;
} catch (Exception ex) {
string errMsg = $"An exception occurred when attempting to get approver user IDs. Exception: {ex.Message}";
_logger.LogError(errMsg);
throw;
}
}
}

View File

@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Components.Forms;
using NLog;
namespace MesaFabApproval.API.Utilities;
public class FileUtilities {
private static readonly Logger _logger = NLog.LogManager.GetCurrentClassLogger();
public static async Task SaveFileToFileSystem(IFormFile file, string path) {
try {
_logger.Info($"Attempting to save file to file system");
if (file is null) throw new ArgumentNullException("File cannot be null");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (File.Exists(path)) throw new Exception($"A file already exists with name {file.FileName}");
string? directoryPath = Path.GetDirectoryName(path);
if (!string.IsNullOrWhiteSpace(directoryPath))
Directory.CreateDirectory(directoryPath);
using (Stream stream = File.Create(path)) {
await file.CopyToAsync(stream);
}
} catch (Exception ex) {
_logger.Error($"Unable to save file to file system, because {ex.Message}");
throw;
}
}
}

View File

@ -0,0 +1,3 @@
{
"AllowedHosts": "*"
}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<targets>
<target name="asyncLog" xsi:type="AsyncWrapper">
<target
name="appLog"
xsi:type="File"
fileName="d:\logs\MesaFabApproval.API\log.txt"
archiveFilename="d:\logs\MesaFabApproval.API\archive\log-${shortdate}.txt"
maxArchiveFiles="30"
archiveEvery="Day"
/>
<target
name="consoleLog"
xsi:type="Console"
/>
</target>
</targets>
<rules>
<logger name="Microsoft.*" finalMinLevel="Warn" />
<logger name="Microsoft.AspNetCore.HttpLogging.*" finalMinLevel="Info" />
<logger name="System.Net.Http.HttpClient.*" finalMinLevel="Warn" />
<logger name="*" minlevel="Info" writeTo="consoleLog, appLog" />
</rules>
</nlog>

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1,30 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net8.0/MesaFabApproval.API.dll",
"args": [],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
},
{
"type": "node",
"request": "launch",
"name": "node Launch Current Opened File",
"program": "${file}"
}
]
}

View File

@ -0,0 +1,6 @@
# mklink
```bash 1731705389065 = 638673021890650000 = Fri Nov 15 2024 14:16:28 GMT-0700 (Mountain Standard Time)
mklink /J "L:\DevOps\Mesa_FI\MesaFabApproval\MesaFabApproval.API\.vscode\.UserSecrets" "C:\Users\phares\AppData\Roaming\Microsoft\UserSecrets\0b98e1f2-95ed-4edd-8149-58cce51ca059"
dotnet run --urls=https://localhost:7114/ -C Release
```

View File

@ -0,0 +1,424 @@
{
"[markdown]": {
"editor.wordWrap": "off"
},
"files.exclude": {
"**/.git": false,
"**/node_modules": true
},
"files.watcherExclude": {
"**/node_modules": true
},
"cSpell.words": [
"abutton",
"accessibilities",
"accodingly",
"acknowledgmentby",
"Acks",
"actionsheet",
"Additonal",
"Addtional",
"againm",
"agendaview",
"Antlr",
"Appover",
"Appprrovers",
"Approvalog",
"Aprovers",
"Aproverslist",
"asax",
"aspnetmvc",
"Assignedto",
"Atachments",
"Attachemnt",
"Attachemnts",
"Attchment",
"auditee",
"Auditee",
"Auditees",
"automaically",
"Autthorized",
"beacuase",
"beforeunload",
"Belguim",
"bfound",
"bgcolor",
"Bies",
"binded",
"blackbackground",
"blackhover",
"blackpressed",
"blueenergy",
"blueopal",
"buttongroup",
"BYMRB",
"bytesgot",
"CAID",
"casection",
"CAXXXX",
"CCPCR",
"CCPCRB",
"cellspacing",
"Cheeso's",
"chkbx",
"Clib",
"colorpicker",
"columnmenu",
"columnsreorder",
"columnsresize",
"comming",
"Containmen",
"Copmments",
"correctiv",
"Correctivet",
"Creat",
"currentd",
"Cyle",
"dadada",
"darkbluehover",
"darkbluepressed",
"darkred",
"datafields",
"datasource",
"dataviz",
"datepicker",
"datetimepicker",
"dayview",
"Deletet",
"Delgation",
"DENITED",
"Deparmtent",
"departmentids",
"Descirption",
"devprog",
"dfeffc",
"Disp",
"Dispo",
"Dispoitio",
"Dispos",
"Dispositon",
"Dispostion",
"Dispostions",
"dispotypevalidation",
"Docbase",
"Documentum",
"Documetum",
"dont",
"downlaoded",
"draganddrop",
"dragcancel",
"dropdownlist",
"Eamils",
"ECNPCRB",
"Ecns",
"edmx",
"EECN",
"emai",
"emailparams",
"Emergrncy",
"energyblue",
"Eran",
"Esql",
"ETECN",
"EXCELOPENXML",
"existinglocation",
"Expando",
"extrafield",
"fadc",
"fbec",
"fcfdfd",
"fdff",
"fece",
"feeebd",
"ffdc",
"ffdd",
"fieldset",
"FILIPE",
"filtermenu",
"Fldr",
"flintstone",
"FLOWLOCS",
"FMEA",
"ftplib",
"FTPSPN",
"GETDATE",
"gitea",
"globaldocudms",
"glyphicons",
"groupable",
"Guids",
"halflings",
"Hexsize",
"highcontrast",
"Hmac",
"holdsteps",
"hostspecific",
"icenium",
"IECN",
"imagebrowser",
"IMRB",
"Infineon",
"Insertd",
"inverseicons",
"IPCRB",
"ISADMIN",
"islast",
"ISNULL",
"ITAR",
"jquery",
"jqueryval",
"jqwidgets",
"jqxbuttongroup",
"jqxbuttons",
"jqxcalendar",
"jqxchart",
"jqxcheckbox",
"jqxcolorpicker",
"jqxcombobox",
"jqxcore",
"jqxdata",
"jqxdatatable",
"jqxdatetimeinput",
"jqxdocking",
"jqxdockpanel",
"jqxdragdrop",
"jqxdropdownbutton",
"jqxdropdownlist",
"jqxexpander",
"jqxgauge",
"jqxgrid",
"jqxinput",
"jqxknockout",
"jqxlistbox",
"jqxlistmenu",
"jqxmaskedinput",
"jqxmenu",
"jqxnavigationbar",
"jqxnumberinput",
"jqxpanel",
"jqxpasswordinput",
"jqxprogressbar",
"jqxradiobutton",
"jqxrangeselector",
"jqxrating",
"jqxresponse",
"jqxscrollbar",
"jqxscrollview",
"jqxslider",
"jqxsplitter",
"jqxswitchbutton",
"jqxtabs",
"jqxtooltip",
"jqxtouch",
"jqxtree",
"jqxtreegrid",
"jqxtreemap",
"jqxvalidator",
"jqxwindow",
"kendogridcustom",
"kendoui",
"labelelement",
"labelledby",
"Leanred",
"lightgray",
"linkbutton",
"Linq",
"Listdiv",
"listview",
"Lnks",
"localfilename",
"loclist",
"logis",
"logtext",
"loopmis",
"lotdispo",
"LOTDISPSITION",
"Lotfile",
"lotlist",
"lotstatusoption",
"LTRIM",
"MADUREIRA",
"mailrelay",
"MDTM",
"meego",
"meetingid",
"menubutton",
"mesafi",
"metroblack",
"metrodark",
"miliseconds",
"modalview",
"modernizr",
"Modernizr",
"monthview",
"MRBIs",
"Mrbs",
"msecs",
"multipleextended",
"Navigatable",
"nbsp",
"newbase",
"newchange",
"newdi",
"newfilename",
"newsource",
"Newtonsoft",
"notications",
"Notifcation",
"Notifyf",
"NTLM",
"Nullcc",
"numerictextbox",
"objdata",
"OCAP",
"occured",
"odata",
"oldfilename",
"OLHOLD",
"onclick",
"onmousemove",
"OPDESC",
"OPENQUERY",
"Oper",
"operationslist",
"Orginator",
"Originatorname",
"Ouellette",
"Owin",
"pageable",
"Pageable",
"panelbar",
"parentid",
"parminput",
"parms",
"Parms",
"particula",
"pasv",
"PASV",
"PATINDEX",
"PCRB",
"PCRBID",
"pcrvalues",
"pdbonly",
"Preventitive",
"preventivet",
"Prevetative",
"proces",
"Processedl",
"procs",
"productfamilies",
"progess",
"progressbar",
"qrcode",
"Quanityt",
"rangebar",
"Recep",
"Recepient",
"recieved",
"recordlock",
"remotefilename",
"reorderable",
"reportform",
"reportslist",
"reportslistdiv",
"Reqquired",
"Reqs",
"Requiest",
"Responsibles",
"RETR",
"Revisioing",
"Revisioned",
"Revison",
"rgba",
"rkotian",
"RNFR",
"RNTO",
"Roless",
"roundbg",
"RTRIM",
"SAMDB",
"scroller",
"scrollview",
"seleced",
"selectionlog",
"Selectpart",
"sess",
"Sfisharepoint",
"shinyblack",
"showpassword",
"SIGNON",
"simpleparser",
"slddrw",
"sldprt",
"sortasc",
"sortascbutton",
"sortdesc",
"sortdescbutton",
"sortremove",
"sparkline",
"splitview",
"SPNMRB",
"SPNPDB",
"SSRS",
"Sssign",
"Staus",
"stylesheet",
"Submited",
"subrole",
"subroles",
"Succefully",
"Succesfully",
"sucessfully",
"SURP",
"Swashbuckle",
"SWRN",
"tabindex",
"tabstrip",
"Tahoma",
"taskcompleted",
"Tasklist",
"Taveler",
"TECN",
"TECNs",
"TEMIRWAP",
"tempecd",
"tempimplement",
"templabel",
"tempvalue",
"TEMSA",
"timepicker",
"Tobe",
"Toplevel",
"Totrav",
"trainingby",
"Traininglist",
"traininglistdiv",
"transanction",
"Trav",
"Traveller",
"Traverler",
"TRAVLELER",
"Travler",
"TREEVIEW",
"trigerred",
"ttinclude",
"Uhandled",
"Updat",
"Uplaod",
"Upto",
"userevents",
"userids",
"userlist",
"Validatable",
"valueelement",
"Variabls",
"Verdana",
"vgrid",
"viewmodel",
"vsdoc",
"whethere",
"windowsphone",
"Winsock",
"worlflow"
]
}

View File

@ -0,0 +1,121 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "User Secrets Init",
"command": "dotnet",
"type": "process",
"args": [
"user-secrets",
"-p",
"${workspaceFolder}/MesaFabApproval.Client.csproj",
"init"
],
"problemMatcher": "$msCompile"
},
{
"label": "Format",
"command": "dotnet",
"type": "process",
"args": [
"format",
"--report",
".vscode",
"--verbosity",
"detailed",
"--severity",
"warn"
],
"problemMatcher": "$msCompile"
},
{
"label": "Format-Whitespaces",
"command": "dotnet",
"type": "process",
"args": [
"format",
"whitespace"
],
"problemMatcher": "$msCompile"
},
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/MesaFabApproval.Client.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "testDebug",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/MesaFabApproval.Client.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "testRelease",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/MesaFabApproval.Client.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"-c",
"Release"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/MesaFabApproval.Client.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/MesaFabApproval.Client.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "Publish AOT",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"-r",
"win-x64",
"-c",
"Release",
"-p:PublishAot=true",
"${workspaceFolder}/MesaFabApproval.Client.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@ -0,0 +1,26 @@
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" >
<NotAuthorized>
@if (context.User.Identity?.IsAuthenticated != true) {
<RedirectToLogin />
} else {
<p role="alert">You are not authorized to access this resource.</p>
}
</NotAuthorized>
<Authorizing>
<MudProgressCircular Color="Color.Tertiary" Indeterminate="true" />
<div>Authorizing...</div>
</Authorizing>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>

View File

@ -0,0 +1,8 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>", Scope = "member", Target = "~M:MesaFabApproval.Client.Services.AuthenticationService.AttemptLocalUserAuth~System.Threading.Tasks.Task{System.Security.Claims.ClaimsPrincipal}")]

View File

@ -0,0 +1,124 @@
@using System.Text.Json
@using System.Text
@inherits LayoutComponentBase
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject IAuthenticationService authenticationService
@inject IConfiguration Configuration
@inject IMemoryCache cache
@inject IJSRuntime jsRuntime
@inject IHttpClientFactory httpClientFactory
@inject ISnackbar snackbar
@inject NavigationManager navManager
<MudThemeProvider />
<MudDialogProvider />
<MudSnackbarProvider />
<MudPopoverProvider />
<div style="height: 100vh;">
<MudLayout>
<MudAppBar Elevation="1" Color="Color.Info">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@((e) => DrawerToggle())" />
<MudText Typo="Typo.h5" Class="ml-3">Mesa Fab Approval</MudText>
@if (authStateProvider.CurrentUser is not null) {
<MudSpacer />
<MudText Typo="Typo.h6" Class="mr-3">@authStateProvider.CurrentUser.FirstName @authStateProvider.CurrentUser.LastName</MudText>
<MudIconButton Variant="Variant.Filled"
Color="Color.Tertiary"
OnClick=Logout
Edge="Edge.End"
Icon="@Icons.Material.Filled.Logout" />
}
</MudAppBar>
<MudDrawer @bind-Open="_drawerOpen" ClipMode="DrawerClipMode.Always" Elevation="2">
<MudNavMenu Color="Color.Info" Bordered="true" Class="d-flex flex-column justify-center p-1 gap-1">
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Href="@Configuration["OldFabApprovalUrl"]"
Target="_blank"
StartIcon="@Icons.Material.Filled.Home">
Return to Main Site
</MudButton>
<MudDivider Class="my-1" />
@if (authStateProvider.CurrentUser is not null) {
<MudNavGroup Title="Create New">
<MudNavLink OnClick="@(() => GoTo("mrb/new"))">Create New MRB</MudNavLink>
<MudNavLink OnClick="@(() => GoTo("pcrb/new"))">Create New PCRB</MudNavLink>
</MudNavGroup>
<MudNavLink OnClick="@(() => GoTo(""))" Icon="@Icons.Material.Filled.Dashboard">Dashboard</MudNavLink>
<MudNavLink OnClick="@(() => GoTo("mrb/all"))" Icon="@Icons.Material.Filled.Ballot">MRB List</MudNavLink>
<MudNavLink OnClick="@(() => GoTo("pcrb/all"))" Icon="@Icons.Material.Filled.Ballot">PCRB List</MudNavLink>
}
</MudNavMenu>
</MudDrawer>
<div style="display: flex; flex-flow: column; height: 100%;">
<MudMainContent Style="@($"background:#E0E0E0; flex-grow: 1;")">
@Body
</MudMainContent>
</div>
</MudLayout>
</div>
@code {
bool _drawerOpen = true;
void DrawerToggle() {
_drawerOpen = !_drawerOpen;
}
void Logout() {
authStateProvider.Logout();
}
private void GoTo(string page) {
DrawerToggle();
cache.Set("redirectUrl", page);
navManager.NavigateTo(page);
}
private async Task GoToExternal(string url, string content) {
IJSObjectReference windowModule = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./js/OpenInNewWindow.js");
await windowModule.InvokeAsync<object>("OpenInNewWindow", url, content);
}
private async Task GoToOldSite() {
try {
User? currentUser = authStateProvider.CurrentUser;
AuthTokens? authTokens = await authenticationService.GetAuthTokens();
if (currentUser is null || authTokens is null) {
await authStateProvider.Logout();
navManager.NavigateTo("login");
return;
}
AuthAttempt authAttempt = new() {
LoginID = currentUser.LoginID,
AuthTokens = authTokens
};
HttpClient httpClient = httpClientFactory.CreateClient("OldSite");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "Account/ExternalAuthSetup");
request.Content = new StringContent(JsonSerializer.Serialize(authAttempt),
Encoding.UTF8,
"application/json");
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
if (httpResponseMessage.IsSuccessStatusCode) {
snackbar.Add("Old site auth setup successful", Severity.Success);
} else {
snackbar.Add($"Old site auth setup failed, because {httpResponseMessage.ReasonPhrase}", Severity.Error);
}
await GoToExternal($"{Configuration["OldFabApprovalUrl"]}", "");
} catch (Exception ex) {
snackbar.Add($"Unable to go to old site, because {ex.Message}", Severity.Error);
}
}
}

View File

@ -0,0 +1,77 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}

View File

@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Watch Include="**\*.razor" />
</ItemGroup>
<ItemGroup>
<Watch Remove="Pages\Components\PCRBApproverForm.razor" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.8" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="MudBlazor" Version="7.6.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MesaFabApproval.Shared\MesaFabApproval.Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,65 @@
@page "/redirect"
@attribute [AllowAnonymous]
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject IAuthenticationService authService
@inject IUserService userService
@inject ISnackbar snackbar
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject NavigationManager navigationManager
@code {
private string? _jwt;
private string? _refreshToken;
private string? _redirectPath;
protected override async Task OnParametersSetAsync() {
try {
Uri uri = navigationManager.ToAbsoluteUri(navigationManager.Uri);
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("jwt", out var jwt)) {
_jwt = System.Net.WebUtility.UrlDecode(jwt);
}
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("refreshToken", out var refreshToken)) {
_refreshToken = System.Net.WebUtility.UrlDecode(refreshToken);
}
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("redirectPath", out var redirectPath)) {
_redirectPath = redirectPath.ToString();
}
if (!string.IsNullOrWhiteSpace(_jwt) && !string.IsNullOrWhiteSpace(_refreshToken)) {
await authService.SetTokens(_jwt, _refreshToken);
ClaimsPrincipal principal = authService.GetClaimsPrincipalFromJwt(_jwt);
string loginId = userService.GetLoginIdFromClaimsPrincipal(principal);
await authService.ClearCurrentUser();
await authService.ClearTokens();
await authService.SetLoginId(loginId);
await authService.SetTokens(_jwt, _refreshToken);
await authService.SetCurrentUser(null);
await authStateProvider.StateHasChanged(principal);
}
if (authStateProvider.CurrentUser is not null && !string.IsNullOrWhiteSpace(_redirectPath)) {
navigationManager.NavigateTo(_redirectPath);
} else {
await authStateProvider.Logout();
if (!string.IsNullOrWhiteSpace(_redirectPath)) {
navigationManager.NavigateTo($"login/{_redirectPath}");
} else {
navigationManager.NavigateTo("login");
}
}
} catch (Exception ex) {
snackbar.Add($"Redirect failed, because {ex.Message}", Severity.Error);
navigationManager.NavigateTo("login");
}
}
}

View File

@ -0,0 +1,69 @@
@inject ISnackbar snackbar
<MudDialog>
<DialogContent>
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudTextField T="string"
Label="Comments"
Required="true"
RequiredError="You must provide a comment"
@bind-Value="@comments"
@bind-Text="@comments"
Immediate="true"
AutoGrow
AutoFocus/>
</MudForm>
</MudPaper>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=SubmitComments>
@if (processing) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Ok</MudText>
}
</MudButton>
<MudButton Variant="Variant.Filled"
Class="grey text-black m1-auto"
OnClick=Cancel>
Cancel
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
[Parameter]
public string comments { get; set; } = "";
private string[] errors = { };
private bool processing = false;
protected override void OnParametersSet() {
comments = string.Empty;
}
private void SubmitComments() {
processing = true;
try {
if (string.IsNullOrWhiteSpace(comments) || comments.Length < 5)
throw new Exception("Comments must be at least five characters long");
MudDialog.Close(DialogResult.Ok(comments));
} catch (Exception ex) {
snackbar.Add(ex.Message, Severity.Error);
}
processing = false;
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
}

View File

@ -0,0 +1,115 @@
@inject ISnackbar snackbar
@inject IMRBService mrbService
@inject MesaFabApprovalAuthStateProvider authStateProvider
<MudDialog>
<DialogContent>
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudTextField T="string"
Label="Comments"
Required="true"
RequiredError="You must provide a comment"
@bind-Value="@comments"
@bind-Text="@comments"
Immediate="true"
AutoGrow
AutoFocus/>
<MudFileUpload T="IReadOnlyList<IBrowserFile>" OnFilesChanged="AddAttachments">
<ActivatorContent>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
style="margin: auto;"
StartIcon="@Icons.Material.Filled.AttachFile">
@if (attachmentUploadInProcess) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Upload Supporting Document</MudText>
}
</MudButton>
</ActivatorContent>
</MudFileUpload>
</MudForm>
</MudPaper>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=SubmitComments>
@if (processing) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Ok</MudText>
}
</MudButton>
<MudButton Variant="Variant.Filled"
Class="grey text-black m1-auto"
OnClick=Cancel>
Cancel
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
[Parameter]
public string comments { get; set; } = "";
[Parameter]
public int actionId { get; set; } = 0;
private string[] errors = { };
private bool processing = false;
private bool attachmentUploadInProcess = false;
protected override void OnParametersSet() {
comments = string.Empty;
}
private void SubmitComments() {
processing = true;
try {
if (string.IsNullOrWhiteSpace(comments) || comments.Length < 5)
throw new Exception("Comments must be at least five characters long");
MudDialog.Close(DialogResult.Ok(comments));
} catch (Exception ex) {
snackbar.Add(ex.Message, Severity.Error);
}
processing = false;
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
private async Task AddAttachments(InputFileChangeEventArgs args) {
attachmentUploadInProcess = true;
try {
if (actionId <= 0)
throw new Exception($"{actionId} is not a valid MRB action ID");
IReadOnlyList<IBrowserFile> attachments = args.GetMultipleFiles();
if (authStateProvider.CurrentUser is not null) {
await mrbService.UploadActionAttachments(attachments, actionId);
await mrbService.GetAllActionAttachmentsForMRB(actionId, true);
attachmentUploadInProcess = false;
snackbar.Add("Attachments successfully uploaded", Severity.Success);
StateHasChanged();
}
} catch (Exception ex) {
attachmentUploadInProcess = false;
snackbar.Add($"Unable to upload attachments, because {ex.Message}", Severity.Error);
}
}
}

View File

@ -0,0 +1,327 @@
@inject IMRBService mrbService
@inject ISnackbar snackbar
@inject ICustomerService customerService
<MudDialog>
<DialogContent>
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudSelect T="string"
Label="Action"
Disabled="@(!string.IsNullOrWhiteSpace(mrbAction.Action))"
Required="true"
RequiredError="You must select an action!"
@bind-Value="@mrbAction.Action"
Text="@mrbAction.Action">
<MudSelectItem Value="@("Block")" />
<MudSelectItem Value="@("Convert")" />
<MudSelectItem Value="@("Recall")" />
<MudSelectItem Value="@("Scrap")" />
<MudSelectItem Value="@("Unblock")" />
<MudSelectItem Value="@("Waiver")" />
</MudSelect>
@if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) {
<MudSelect T="string"
Label="Convert From Customer"
Required
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=convertFromCustomer
Text="@convertFromCustomer">
@foreach (string customer in customerNames) {
<MudSelectItem Value="@(customer)" />
}
</MudSelect>
<MudTextField @bind-Value="@convertFromPart"
Label="Convert From Part Number"
Required
RequiredError="Part number required!"
Text="@convertFromPart" />
<MudSelect T="string"
Label="Convert To Customer"
Required
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=convertToCustomer
Text="@convertToCustomer">
@foreach (string customer in customerNames) {
<MudSelectItem Value="@(customer)" />
}
</MudSelect>
<MudTextField @bind-Value="@convertToPart"
Label="Convert To Part Number"
Required
RequiredError="Part number required!"
Text="@convertToPart" />
} else {
<MudSelect T="string"
Label="Affected Customer"
Required
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=mrbAction.Customer
Text="@mrbAction.Customer">
@foreach (string customer in customerNames) {
<MudSelectItem Value="@(customer)" />
}
</MudSelect>
<MudAutocomplete T="string"
Label="Part Number"
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=mrbAction.PartNumber
Text="@mrbAction.PartNumber"
SearchFunc="@PartNumberSearch"
ResetValueOnEmptyText
CoerceText />
}
<MudTextField T="int"
InputType="@InputType.Number"
Label="Qty"
Required="true"
RequiredError="You must supply a quantity!"
@bind-Value=mrbAction.Quantity
Text="@mrbAction.Quantity.ToString()" />
<MudAutocomplete T="string"
Label="Batch Number / Lot Number"
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=mrbAction.LotNumber
Text="@mrbAction.LotNumber"
SearchFunc="@LotNumberSearch"
ResetValueOnEmptyText
CoerceText />
@if (mrbAction.Action.Equals("Scrap")) {
<MudSelect T="string"
Label="Justification"
Required
RequiredError="Justification required!"
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=mrbAction.Justification
Text="@mrbAction.Justification">
<MudSelectItem Value="@("Obsolete")" />
<MudSelectItem Value="@("Aged Material")" />
<MudSelectItem Value="@("Out of Specification")" />
</MudSelect>
}
</MudForm>
</MudPaper>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=SaveMRBAction>
@if (processingSave) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Save</MudText>
}
</MudButton>
@if (mrbAction is not null && mrbAction.ActionID > 0) {
<MudButton Variant="Variant.Filled"
Color="Color.Secondary"
Disabled="@(mrbAction is null || (mrbAction is not null && mrbAction.ActionID <= 0))"
Class="m1-auto"
OnClick=DeleteMRBAction>
@if (processingDelete) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Delete</MudText>
}
</MudButton>
}
<MudButton Variant="Variant.Filled"
Class="grey text-black m1-auto"
OnClick=Cancel>
Cancel
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
[Parameter]
public MRBAction mrbAction { get; set; }
private IEnumerable<MRBAction>? actions = null;
private MRBAction lastAction = null;
private IEnumerable<string> customerNames = new List<string>();
private string convertFromCustomer = string.Empty;
private string convertFromPart = string.Empty;
private string convertToCustomer = string.Empty;
private string convertToPart = string.Empty;
private bool isVisible { get; set; }
private string[] errors = { };
private bool processingSave = false;
private bool processingDelete = false;
protected override async Task OnParametersSetAsync() {
isVisible = true;
if (mrbAction is null) {
snackbar.Add("MRB action cannot be null", Severity.Warning);
MudDialog.Cancel();
} else {
actions = (await mrbService.GetMRBActionsForMRB(mrbAction.MRBNumber, false)).OrderByDescending(a => a.ActionID);
if (actions is not null && actions.Count() > 0) {
if (string.IsNullOrWhiteSpace(mrbAction.Action))
mrbAction.Action = actions.First().Action;
if (string.IsNullOrWhiteSpace(mrbAction.Customer))
mrbAction.Customer = actions.First().Customer;
if (string.IsNullOrWhiteSpace(mrbAction.LotNumber))
mrbAction.LotNumber = actions.First().LotNumber;
if (string.IsNullOrWhiteSpace(mrbAction.PartNumber))
mrbAction.PartNumber = actions.First().PartNumber;
if (string.IsNullOrWhiteSpace(mrbAction.Justification))
mrbAction.Justification = actions.First().Justification;
if (mrbAction.Quantity == 0)
mrbAction.Quantity = actions.First().Quantity;
if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) {
string[] convertFrom = actions.First().ConvertFrom.Split(" ");
if (convertFrom.Length > 1) {
convertFromCustomer = convertFrom[0];
foreach (string partStr in convertFrom.Skip(1))
convertFromPart += partStr;
}
string[] convertTo = actions.First().ConvertTo.Split(" ");
if (convertTo.Length > 1) {
convertToCustomer = convertTo[0];
foreach (string partStr in convertTo.Skip(1))
convertToPart += partStr;
}
}
}
}
if (customerNames is null || customerNames.Count() <= 0)
customerNames = await customerService.GetAllCustomerNames();
StateHasChanged();
}
private bool FormIsValid() {
bool actionIsValid = mrbAction.Action.Equals("Block") || mrbAction.Action.Equals("Convert") ||
mrbAction.Action.Equals("Other") || mrbAction.Action.Equals("Recall") || mrbAction.Action.Equals("Scrap") ||
mrbAction.Action.Equals("Unblock") || mrbAction.Action.Equals("Waiver");
actionIsValid = actionIsValid && !string.IsNullOrWhiteSpace(mrbAction.Customer) &&
!string.IsNullOrWhiteSpace(mrbAction.PartNumber) &&
!string.IsNullOrWhiteSpace(mrbAction.LotNumber);
actionIsValid = actionIsValid && mrbAction.Quantity > 0;
if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) {
actionIsValid = actionIsValid && !string.IsNullOrWhiteSpace(convertFromCustomer) &&
!string.IsNullOrWhiteSpace(convertFromPart) &&
!string.IsNullOrWhiteSpace(convertToCustomer) &&
!string.IsNullOrWhiteSpace(convertToPart);
}
if (mrbAction.Action.Equals("Scrap", StringComparison.InvariantCultureIgnoreCase))
actionIsValid = actionIsValid && !string.IsNullOrWhiteSpace(mrbAction.Justification);
return actionIsValid;
}
private async void SaveMRBAction() {
processingSave = true;
try {
if (!FormIsValid()) throw new Exception("You must complete the form before saving!");
if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) {
mrbAction.ConvertFrom = $"{convertFromCustomer} {convertFromPart}";
mrbAction.ConvertTo = $"{convertToCustomer} {convertToPart}";
}
if (mrbAction.MRBNumber > 0) {
if (mrbAction.ActionID <= 0) {
await mrbService.CreateMRBAction(mrbAction);
snackbar.Add("MRB action created", Severity.Success);
} else {
await mrbService.UpdateMRBAction(mrbAction);
snackbar.Add("MRB action updated", Severity.Success);
}
actions = (await mrbService.GetMRBActionsForMRB(mrbAction.MRBNumber, true)).OrderByDescending(a => a.ActionID);
} else {
snackbar.Add("MRB action saved", Severity.Success);
}
StateHasChanged();
MudDialog.Close(DialogResult.Ok(mrbAction));
} catch (Exception ex) {
snackbar.Add(ex.Message, Severity.Error);
}
processingSave = false;
}
private async void DeleteMRBAction() {
processingDelete = true;
try {
if (mrbAction is null) throw new Exception("MRB action cannot be null!");
if (mrbAction.ActionID <= 0)
throw new Exception("You cannot delete an action before creating it!");
if (mrbAction.MRBNumber <= 0)
throw new Exception("Invalid MRB number!");
await mrbService.DeleteMRBAction(mrbAction);
snackbar.Add("MRB action successfully deleted", Severity.Success);
StateHasChanged();
MudDialog.Close(DialogResult.Ok<MRBAction>(null));
} catch (Exception ex) {
snackbar.Add(ex.Message, Severity.Error);
}
processingDelete = false;
}
private void Cancel() {
MudDialog.Cancel();
}
private async Task<IEnumerable<string>> PartNumberSearch(string value, CancellationToken token) {
if (actions is null) return new List<string> { value };
if (string.IsNullOrWhiteSpace(value)) return new string[0];
HashSet<string> partNumbers = new();
partNumbers.Add(value);
foreach (MRBAction action in actions) {
if (action.PartNumber.Contains(value, StringComparison.InvariantCultureIgnoreCase))
partNumbers.Add(action.PartNumber);
}
return partNumbers;
}
private async Task<IEnumerable<string>> LotNumberSearch(string value, CancellationToken token) {
if (actions is null) return new List<string> { value };
if (string.IsNullOrWhiteSpace(value)) return new string[0];
HashSet<string> lotNumbers = new();
lotNumbers.Add(value);
foreach (MRBAction action in actions) {
if (action.LotNumber.Contains(value, StringComparison.InvariantCultureIgnoreCase))
lotNumbers.Add(action.LotNumber);
}
return lotNumbers;
}
}

View File

@ -0,0 +1,111 @@
@inject IApprovalService approvalService
@inject ISnackbar snackbar
@inject MesaFabApprovalAuthStateProvider authStateProvider
<MudDialog>
<DialogContent>
@if (availableApprovers is not null) {
<MudPaper Class="m-2 p-2">
<MudSelect T="User"
Label="Select a User"
Required
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=selectedUser
Text="@(selectedUser is null ? "" : selectedUser.GetFullName())">
@foreach (User user in availableApprovers) {
<MudSelectItem Value="@user">
@user.GetFullName()
</MudSelectItem>
}
</MudSelect>
</MudPaper>
}
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=Submit>
<MudText>Submit</MudText>
</MudButton>
<MudButton Variant="Variant.Filled"
Color="Color.Secondary"
Class="m1-auto"
OnClick=Cancel>
<MudText>Cancel</MudText>
</MudButton>
</DialogActions>
</MudDialog>
<MudOverlay Visible=processing DarkBackground="true" AutoClose="false">
<MudProgressCircular Color="Color.Info" Size="Size.Medium" Indeterminate="true" />
</MudOverlay>
@code {
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
[Parameter]
public User selectedUser { get; set; }
private bool processing = false;
private HashSet<User> availableApprovers = new();
protected override async Task OnInitializedAsync() {
try {
processing = true;
string roleName = "QA_PRE_APPROVAL";
string subRoleName = "QA_PRE_APPROVAL";
IEnumerable<User> qaApprovers = await GetApprovalGroupMembersForRoleAndSubRole(roleName, subRoleName);
foreach (User approver in qaApprovers)
availableApprovers.Add(approver);
roleName = "MRB Approver";
subRoleName = "MRBApprover";
IEnumerable<User> mrbApprovers = await GetApprovalGroupMembersForRoleAndSubRole(roleName, subRoleName);
foreach (User approver in mrbApprovers)
availableApprovers.Add(approver);
selectedUser = availableApprovers.First();
processing = false;
} catch (Exception ex) {
processing = false;
snackbar.Add($"Unable to get all approvers, because {ex.Message}", Severity.Error);
}
}
private void Submit() {
MudDialog.Close(DialogResult.Ok(selectedUser));
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
private async Task<IEnumerable<User>> GetApprovalGroupMembersForRoleAndSubRole(string roleName, string subRoleName) {
HashSet<User> members = new();
int roleId = await approvalService.GetRoleIdForRoleName(roleName);
if (roleId <= 0) throw new Exception($"could not find {roleName} role ID");
IEnumerable<SubRole> subRoles = await approvalService.GetSubRolesForSubRoleName(subRoleName, roleId);
foreach (SubRole subRole in subRoles) {
IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID);
foreach (User member in subRoleMembers) {
members.Add(member);
}
}
return members;
}
}

View File

@ -0,0 +1,151 @@
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject NavigationManager navigationManager
@inject IPCRBService pcrbService
@inject IUserService userService
@inject IECNService ecnService
@inject ISnackbar snackbar
<MudDialog>
<DialogContent>
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudTextField T="string"
Label="Document Type"
@bind-Value="@document.DocType"
@bind-Text="@document.DocType"
Disabled
AutoGrow />
<MudTextField Label="Document Numbers - Rev. & Title"
@bind-Value="@document.DocNumbers"
@bind-Text="@document.DocNumbers"
Immediate
AutoGrow
AutoFocus />
@if (DocNumberIsNA()) {
<MudTextField Label="Comments"
@bind-Value="@document.Comment"
@bind-Text="@document.Comment"
Required
RequiredError="You must provide a comment"
Immediate
AutoGrow />
} else {
<MudTextField @bind-Value="@document.ECNNumber"
Required
RequiredError="You must provide a valid ECN#"
Clearable
Variant="Variant.Outlined"
InputType="@InputType.Number"
Validation="@(new Func<int, Task<string>>(ECNNoIsValid))"
Label="ECN#"
Immediate
AutoGrow />
}
<MudCheckBox Label="Complete"
Color="Color.Tertiary"
@bind-Value=complete
LabelPosition="LabelPosition.Start" />
</MudForm>
</MudPaper>
</DialogContent>
<DialogActions>
@if ((DocNumberIsNA() && !string.IsNullOrWhiteSpace(document.Comment)) ||
(!DocNumberIsNA() && ecnNoIsValid)) {
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=Save>
@if (saveInProcess) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Save</MudText>
}
</MudButton>
}
<MudButton Variant="Variant.Filled"
Class="grey text-black m1-auto"
OnClick=Cancel>
Cancel
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
[Parameter]
public required PCR3Document document { get; set; }
private string[] errors = { };
private bool complete = false;
private bool saveInProcess = false;
private bool ecnNoIsValid = true;
protected override async Task OnParametersSetAsync() {
complete = document.CompletedByID > 0;
}
private async Task Save() {
saveInProcess = true;
try {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
return;
}
if (!complete) {
document.CompletedByID = 0;
document.CompletedBy = null;
document.CompletedDate = DateTimeUtilities.MAX_DT;
}
if (complete && document.CompletedByID <= 0) {
document.CompletedByID = authStateProvider.CurrentUser.UserID;
document.CompletedBy = authStateProvider.CurrentUser;
document.CompletedDate = DateTime.Now;
}
if (!DocNumberIsNA() && !ecnNoIsValid)
throw new Exception($"{document.ECNNumber} is not a valid ECN#");
if (DocNumberIsNA() && string.IsNullOrWhiteSpace(document.Comment))
throw new Exception("you must provide a comment");
await pcrbService.UpdatePCR3Document(document);
await pcrbService.GetPCR3DocumentsForPlanNumber(document.PlanNumber, true);
saveInProcess = false;
MudDialog.Close(DialogResult.Ok(document));
} catch (Exception ex) {
saveInProcess = false;
snackbar.Add($"Unable to save document, because {ex.Message}", Severity.Error);
}
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
private bool DocNumberIsNA() {
if (document.DocNumbers.ToLower().Equals("na") ||
document.DocNumbers.ToLower().Equals("n/a")) {
return true;
}
return false;
}
private async Task<string> ECNNoIsValid(int ecnNumber) {
string? result = await ecnService.ECNNumberIsValidStr(ecnNumber);
if (result is null) ecnNoIsValid = true;
else ecnNoIsValid = false;
StateHasChanged();
return result;
}
}

View File

@ -0,0 +1,201 @@
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject NavigationManager navigationManager
@inject IPCRBService pcrbService
@inject IUserService userService
@inject ISnackbar snackbar
<MudDialog>
<DialogContent>
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudTextField T="string"
Label="Action"
Required
RequiredError="Enter action item"
@bind-Value="@name"
@bind-Text="@name"
Immediate
Clearable
AutoGrow
AutoFocus />
<MudCheckBox Label="Gating"
Color="Color.Tertiary"
@bind-Value=gating
LabelPosition="LabelPosition.Start" />
<MudCheckBox Label="Closed"
Color="Color.Tertiary"
@bind-Value="@closedStatus"
LabelPosition="LabelPosition.Start" />
@if (closedStatus) {
<MudDatePicker Label="Closed Date"
Color="Color.Tertiary"
@bind-Date="@closedDate"
Clearable
MinDate="@DateTimeUtilities.MIN_DT"
MaxDate="@DateTimeUtilities.MAX_DT"
Placeholder="Select a closed date" />
}
<MudSelect T="User"
Label="Responsible Person"
Variant="Variant.Outlined"
Required
RequiredError="You must select a responsible person"
Clearable
AnchorOrigin="Origin.BottomCenter"
ToStringFunc="@UserToNameConverter"
@bind-Value=@responsiblePerson>
@foreach (User user in allActiveUsers.OrderBy(u => u.FirstName)) {
<MudSelectItem T="User" Value="@(user)" />
}
</MudSelect>
</MudForm>
</MudPaper>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=Save>
@if (saveActionItemInProcess) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Submit</MudText>
}
</MudButton>
<MudButton Variant="Variant.Filled"
Class="grey text-black m1-auto"
OnClick=Cancel>
Cancel
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
[Parameter]
public int planNumber { get; set; } = 0;
[Parameter]
public int step { get; set; } = 0;
[Parameter]
public PCRBActionItem? actionItem { get; set; } = null;
[Parameter]
public IEnumerable<User> allActiveUsers { get; set; }
private string[] errors = { };
private string name = "";
private bool gating = false;
private DateTime? closedDate = DateTimeUtilities.MAX_DT;
private bool closedStatus = false;
private User? responsiblePerson = null;
private bool saveActionItemInProcess = false;
protected override async Task OnParametersSetAsync() {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
}
if (planNumber <= 0) {
snackbar.Add($"{planNumber} is not a valid PCRB plan#", Severity.Error);
MudDialog.Close(DialogResult.Cancel());
}
if (allActiveUsers is null || allActiveUsers.Count() <= 0)
allActiveUsers = await userService.GetAllActiveUsers();
if (actionItem is not null) {
name = actionItem.Name;
gating = actionItem.Gating;
closedStatus = actionItem.ClosedStatus;
closedDate = actionItem.ClosedDate;
if (closedDate.Equals(DateTimeUtilities.MAX_DT)) {
closedDate = DateTime.Now;
}
if (actionItem.ResponsiblePersonID > 0) {
if (actionItem.ResponsiblePerson is not null) responsiblePerson = actionItem.ResponsiblePerson;
else responsiblePerson = await userService.GetUserByUserId(actionItem.ResponsiblePersonID);
actionItem.ResponsiblePerson = responsiblePerson;
}
} else {
name = "";
gating = false;
closedStatus = false;
closedDate = DateTime.Now;
responsiblePerson = null;
}
}
private async Task Save() {
saveActionItemInProcess = true;
try {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
}
if (string.IsNullOrWhiteSpace(name)) throw new Exception("name missing");
if (actionItem is null) {
actionItem = new() {
Name = name,
UploadedBy = authStateProvider.CurrentUser,
UploadedByID = authStateProvider.CurrentUser.UserID,
PlanNumber = planNumber,
Step = step,
Gating = gating,
ClosedStatus = closedStatus,
ResponsiblePerson = responsiblePerson,
ResponsiblePersonID = responsiblePerson.UserID,
ClosedDate = closedDate,
ClosedBy = closedDate is null || closedDate >= DateTimeUtilities.MAX_DT ? null : authStateProvider.CurrentUser,
ClosedByID = closedStatus ? authStateProvider.CurrentUser.UserID : 0
};
if (actionItem.ClosedStatus == false) {
actionItem.ClosedDate = DateTimeUtilities.MAX_DT;
}
await pcrbService.CreateNewActionItem(actionItem);
} else {
actionItem.Name = name;
actionItem.Gating = gating;
actionItem.ClosedStatus = closedStatus;
actionItem.ClosedDate = closedDate;
if (closedStatus) {
actionItem.ClosedBy = authStateProvider.CurrentUser;
actionItem.ClosedByID = authStateProvider.CurrentUser.UserID;
} else {
actionItem.ClosedDate = DateTimeUtilities.MAX_DT;
}
actionItem.ResponsiblePerson = responsiblePerson;
if (responsiblePerson is not null)
actionItem.ResponsiblePersonID = responsiblePerson.UserID;
await pcrbService.UpdateActionItem(actionItem);
}
saveActionItemInProcess = false;
MudDialog.Close(DialogResult.Ok(actionItem));
} catch (Exception ex) {
saveActionItemInProcess = false;
snackbar.Add($"Unable to save action item, because {ex.Message}", Severity.Error);
}
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
private Func<User, string> UserToNameConverter = u => u is null ? string.Empty : u.GetFullName();
private Func<PCRBAttachment, string> AttachmentToFileNameConverter = a => a is null ? "" : a.FileName;
}

View File

@ -0,0 +1,250 @@
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject NavigationManager navigationManager
@inject IPCRBService pcrbService
@inject IUserService userService
@inject IApprovalService approvalService
@inject ISnackbar snackbar
<MudDialog>
<DialogContent>
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudSelect T="string"
Label="Job Title"
Required
RequiredError="You must provide a job title"
ValueChanged="@SelectedJobTitleChanged"
@bind-Text="@selectedJobTitle"
Immediate
Disabled="@(approval is not null && !string.IsNullOrWhiteSpace(selectedJobTitle))"
Clearable
AutoFocus >
@foreach (string jt in availableJobTitles) {
<MudSelectItem Value="@jt">@jt</MudSelectItem>
}
</MudSelect>
<MudSelect T="User"
Label="Select a User"
Required
Clearable
Disabled="@(string.IsNullOrWhiteSpace(selectedJobTitle))"
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=selectedUser
ToStringFunc="@UserToNameConverter">
@foreach (User user in availableUsers) {
<MudSelectItem Value="@user">@user.GetFullName()</MudSelectItem>
}
</MudSelect>
</MudForm>
</MudPaper>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=Save>
@if (saveAttendeeInProcess) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Submit</MudText>
}
</MudButton>
<MudButton Variant="Variant.Filled"
Class="grey text-black m1-auto"
OnClick=Cancel>
Cancel
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
[Parameter]
public int planNumber { get; set; } = 0;
[Parameter]
public int step { get; set; } = 0;
[Parameter]
public Approval? approval { get; set; } = null;
private HashSet<string> availableJobTitles = new();
private Dictionary<string, SubRole> jobTitleToSubRoleMap = new();
private Dictionary<string, IEnumerable<User>> jobTitleToAvailableUsersMap = new();
private HashSet<User> availableUsers = new();
private string[] errors = { };
private string selectedJobTitle = "";
private User? selectedUser = null;
private bool saveAttendeeInProcess = false;
protected override async Task OnParametersSetAsync() {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
}
if (planNumber <= 0) {
snackbar.Add($"{planNumber} is not a valid PCRB plan#", Severity.Error);
MudDialog.Close(DialogResult.Cancel());
}
await GetAttendees();
if (approval is not null) {
selectedJobTitle = approval.RoleName;
if (approval.UserID > 0) {
if (approval.User is not null) {
selectedUser = approval.User;
} else {
selectedUser = await userService.GetUserByUserId(approval.UserID);
approval.User = selectedUser;
}
}
SelectedJobTitleChanged(selectedJobTitle);
} else {
selectedJobTitle = "";
selectedUser = null;
}
}
private async Task Save() {
saveAttendeeInProcess = true;
try {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
}
if (string.IsNullOrWhiteSpace(selectedJobTitle)) throw new Exception("job title missing");
if (selectedUser is null) throw new Exception("attendee not selected");
PCRB pcrb = await pcrbService.GetPCRBByPlanNumber(planNumber, false);
if (approval is null) {
jobTitleToSubRoleMap.TryGetValue($"{selectedJobTitle}{selectedUser.UserID}", out SubRole? subRole);
if (subRole is null) throw new Exception($"no approval role found for job title {selectedJobTitle}");
approval = new() {
RoleName = subRole.SubRoleCategoryItem,
SubRole = subRole.SubRoleName,
SubRoleID = subRole.SubRoleID,
IssueID = planNumber,
Step = step,
User = selectedUser,
UserID = selectedUser.UserID,
AssignedDate = DateTimeUtilities.MIN_DT
};
await approvalService.CreateApproval(approval);
} else {
int originalUserId = approval.UserID;
approval.UserID = selectedUser.UserID;
approval.User = selectedUser;
if (originalUserId != approval.UserID) {
if (approval.AssignedDate > DateTimeUtilities.MIN_DT)
approval.AssignedDate = DateTime.Now;
approval.NotifyDate = DateTimeUtilities.MIN_DT;
}
await approvalService.UpdateApproval(approval);
if (originalUserId != approval.UserID)
await pcrbService.NotifyNewApprovals(pcrb);
}
saveAttendeeInProcess = false;
MudDialog.Close(DialogResult.Ok(approval));
} catch (Exception ex) {
saveAttendeeInProcess = false;
snackbar.Add($"Unable to save attendee, because {ex.Message}", Severity.Error);
}
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
private Func<User, string> UserToNameConverter = u => u is null ? string.Empty : u.GetFullName();
private async Task GetAttendees() {
int roleId = await approvalService.GetRoleIdForRoleName("Module Manager");
if (roleId <= 0) throw new Exception($"could not find Director role ID");
availableJobTitles.Clear();
jobTitleToAvailableUsersMap.Clear();
jobTitleToSubRoleMap.Clear();
IEnumerable<SubRole> subRoles = await approvalService.GetSubRolesForSubRoleName("MMSubRole", roleId);
HashSet<string> defaultSubRoleCategoryItems = new() { "Si Production", "Si Engineering", "Quality" };
HashSet<string> unusedSubRoleCategoryItems = new() { "GaN Engineering", "GaN Operations", "Integration" };
foreach (SubRole subRole in subRoles) {
if (approval is null) {
if (!defaultSubRoleCategoryItems.Contains(subRole.SubRoleCategoryItem) &&
!unusedSubRoleCategoryItems.Contains(subRole.SubRoleCategoryItem)) {
availableJobTitles.Add(subRole.SubRoleCategoryItem);
IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID);
jobTitleToAvailableUsersMap.Add(subRole.SubRoleCategoryItem, subRoleMembers);
foreach (User member in subRoleMembers) {
jobTitleToSubRoleMap.Add($"{subRole.SubRoleCategoryItem}{member.UserID}", subRole);
}
}
} else {
if (!unusedSubRoleCategoryItems.Contains(subRole.SubRoleCategoryItem)) {
IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID);
jobTitleToAvailableUsersMap.Add(subRole.SubRoleCategoryItem, subRoleMembers);
foreach (User member in subRoleMembers)
availableUsers.Add(member);
}
}
}
}
private void SelectedJobTitleChanged(string jobTitle) {
selectedJobTitle = jobTitle;
selectedUser = null;
availableUsers.Clear();
if (approval is null) {
if (jobTitleToAvailableUsersMap.TryGetValue(jobTitle, out IEnumerable<User>? jobTitleMembers)) {
if (jobTitleMembers is not null) {
foreach (User member in jobTitleMembers)
availableUsers.Add(member);
}
}
} else {
foreach (IEnumerable<User> memberList in jobTitleToAvailableUsersMap.Values) {
if (memberList is not null) {
foreach(User member in memberList) {
availableUsers.Add(member);
}
}
}
}
StateHasChanged();
}
}

View File

@ -0,0 +1,140 @@
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject NavigationManager navigationManager
@inject IPCRBService pcrbService
@inject ISnackbar snackbar
<MudDialog>
<DialogContent>
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudTextField T="string"
Label="File Name"
Disabled
Immediate
@bind-Value="@fileName"
@bind-Text="@fileName"
AutoGrow />
<MudFileUpload T="IBrowserFile"
FilesChanged="AddFile"
Required
Disabled="@(!string.IsNullOrWhiteSpace(fileName))"
RequiredError="You must select a file">
<ActivatorContent>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
style="margin: auto;"
StartIcon="@Icons.Material.Filled.AttachFile">
@if (addFileInProcess) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Add File</MudText>
}
</MudButton>
</ActivatorContent>
</MudFileUpload>
</MudForm>
</MudPaper>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=Submit>
@if (processing) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
}
else {
<MudText>Submit</MudText>
}
</MudButton>
<MudButton Variant="Variant.Filled"
Class="grey text-black m1-auto"
OnClick=Cancel>
Cancel
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
[Parameter]
public int planNumber { get; set; } = 0;
[Parameter]
public int step { get; set; } = 0;
private string[] errors = { };
private string fileName = "";
private IBrowserFile? file = null;
private bool addFileInProcess = false;
private bool processing = false;
protected override async Task OnParametersSetAsync() {
if (planNumber <= 0) {
snackbar.Add($"{planNumber} is not a valid PCRB plan#", Severity.Error);
MudDialog.Close(DialogResult.Cancel());
}
if (step <= 0) {
snackbar.Add($"{step} is not a valid PCRB stage#", Severity.Error);
MudDialog.Close(DialogResult.Cancel());
}
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
}
}
private void AddFile(IBrowserFile newFile) {
addFileInProcess = true;
file = newFile;
fileName = newFile.Name;
addFileInProcess = false;
}
private async Task Submit() {
processing = true;
try {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
return;
}
if (string.IsNullOrWhiteSpace(fileName))
throw new Exception("file name missing");
if (file is null)
throw new Exception("file is missing");
PCRBAttachment attachment = new() {
Step = step,
UploadDateTime = DateTime.Now,
UploadedByID = authStateProvider.CurrentUser.UserID,
PlanNumber = planNumber,
File = file,
FileName = fileName
};
await pcrbService.UploadAttachment(attachment);
processing = false;
MudDialog.Close(DialogResult.Ok(attachment));
} catch (Exception ex) {
snackbar.Add($"Unable to save document, because {ex.Message}", Severity.Error);
processing = false;
}
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
}

View File

@ -0,0 +1,8 @@
@attribute [AllowAnonymous]
@inject NavigationManager Navigation
@code {
protected override void OnInitialized() {
Navigation.NavigateTo("login");
}
}

View File

@ -0,0 +1,74 @@
@inject IUserService userService
@inject ISnackbar snackbar
@inject MesaFabApprovalAuthStateProvider authStateProvider
<MudDialog>
<DialogContent>
@if (allUsers is not null) {
<MudPaper Class="p-2">
<MudSelect T="User"
Label="Select a User"
Required
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=selectedUser
Text="@(selectedUser is null ? "" : selectedUser.GetFullName())">
@foreach (User user in allUsers) {
<MudSelectItem Value="@user">
@user.GetFullName()
</MudSelectItem>
}
</MudSelect>
</MudPaper>
}
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=Submit>
<MudText>Submit</MudText>
</MudButton>
<MudButton Variant="Variant.Filled"
Color="Color.Secondary"
Class="m1-auto"
OnClick=Cancel>
<MudText>Cancel</MudText>
</MudButton>
</DialogActions>
</MudDialog>
<MudOverlay Visible=processing DarkBackground="true" AutoClose="false">
<MudProgressCircular Color="Color.Info" Size="Size.Medium" Indeterminate="true" />
</MudOverlay>
@code {
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
[Parameter]
public User selectedUser { get; set; }
private bool processing = false;
private IEnumerable<User> allUsers = new List<User>();
protected override async Task OnInitializedAsync() {
try {
processing = true;
selectedUser = authStateProvider.CurrentUser;
allUsers = await userService.GetAllActiveUsers();
processing = false;
} catch (Exception ex) {
processing = false;
snackbar.Add($"Unable to get all users, because {ex.Message}", Severity.Error);
}
}
private void Submit() {
MudDialog.Close(DialogResult.Ok(selectedUser));
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
}

View File

@ -0,0 +1,363 @@
@page "/"
@page "/Dashboard"
@inject IConfiguration Configuration
@inject MesaFabApprovalAuthStateProvider stateProvider
@inject IApprovalService approvalService
@inject IMemoryCache cache
@inject NavigationManager navigationManager
@inject ISnackbar snackbar
@inject IMRBService mrbService
@inject IPCRBService pcrbService
@inject IECNService ecnService
@inject ICAService caService
@inject IJSRuntime jsRuntime
<PageTitle>Dashboard</PageTitle>
<MudPaper Class="p-2 m-2">
<MudText Typo="Typo.h3" Align="Align.Center">Dashboard</MudText>
</MudPaper>
<MudPaper Class="p-2 m-2">
<MudTabs Class="p-2" Rounded Centered Color="Color.Info" MinimumTabWidth="50%">
<MudTabPanel Text="My Active Approvals" Style="min-width:50%; text-align: center;">
@if (stateProvider.CurrentUser is not null && approvalList is not null && !myApprovalsProcessing) {
<MudPaper Outlined="true"
Class="p-2 m-2 d-flex flex-column justify-center">
<MudText Typo="Typo.h4" Align="Align.Center">My Active Approvals</MudText>
<MudDivider DividerType="DividerType.Middle" Class="my-2" />
<MudTable Items="@approvalList"
Class="m-2"
Striped
SortLabel="Sort by">
<HeaderContent>
<MudTh>
<MudTableSortLabel SortBy="new Func<Approval,object>(x=>x.IssueID)">
Issue ID
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<Approval,object>(x=>x.SubRoleCategoryItem)">
Role
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<Approval,object>(x=>x.AssignedDate)">
Assigned Date
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<Approval,object>(x=>x.Step)">
Step
</MudTableSortLabel>
</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Issue ID">
@if (context.IssueID > 0) {
<MudLink OnClick="@(() => FollowLink(context.IssueID))">@context.IssueID</MudLink>
}
</MudTd>
<MudTd DataLabel="Role">@context.SubRoleCategoryItem</MudTd>
<MudTd DataLabel="Assigned Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.AssignedDate)</MudTd>
<MudTd DataLabel="Step">@context.Step</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager />
</PagerContent>
</MudTable>
</MudPaper>
} else {
<MudOverlay Visible DarkBackground="true" AutoClose="false">
<MudText Align="Align.Center" Typo="Typo.h3">Processing</MudText>
<MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" />
</MudOverlay>
}
</MudTabPanel>
<MudTabPanel Text="My MRBs" Style="min-width:50%; text-align: center;">
@if (stateProvider.CurrentUser is not null && myMRBs is not null && !myMrbsProcessing) {
<MudPaper Outlined="true"
Class="p-2 m-2 d-flex flex-column justify-center">
<MudText Typo="Typo.h4" Align="Align.Center">My MRBs</MudText>
<MudDivider DividerType="DividerType.Middle" Class="my-2" />
<MudTable Items="@myMRBs"
Class="m-2"
Striped
SortLabel="Sort by"
Filter="new Func<MRB, bool>(FilterFuncForMRBTable)">
<ToolBarContent>
<MudSpacer />
<MudTextField @bind-Value="mrbSearchString"
Placeholder="Search"
Immediate
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search"
IconSize="Size.Medium"
Class="mt-0" />
</ToolBarContent>
<HeaderContent>
<MudTh>
<MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<MRB,object>(x=>x.MRBNumber)">
MRB#
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.Title)">
Title
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.SubmittedDate)">
Submitted Date
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.ApprovalDate)">
Approval Date
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.CancelDate)">
Cancel Date
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.CloseDate)">
Completed Date
</MudTableSortLabel>
</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="MRB#">
<MudLink OnClick="@(() => GoTo($"mrb/{context.MRBNumber}"))">@context.MRBNumber</MudLink>
</MudTd>
<MudTd DataLabel="Title">@context.Title</MudTd>
<MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.SubmittedDate)</MudTd>
<MudTd DataLabel="Approval Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ApprovalDate)</MudTd>
<MudTd DataLabel="Cancel Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CancelDate)</MudTd>
<MudTd DataLabel="Completed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CloseDate)</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager />
</PagerContent>
</MudTable>
</MudPaper>
} else {
<MudOverlay Visible DarkBackground="true" AutoClose="false">
<MudText Align="Align.Center" Typo="Typo.h3">Processing</MudText>
<MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" />
</MudOverlay>
}
</MudTabPanel>
<MudTabPanel Text="My PCRBs" Style="min-width:50%; text-align: center;">
@if (stateProvider.CurrentUser is not null && myPCRBs is not null && !myPcrbsProcessing) {
<MudPaper Outlined="true"
Class="p-2 m-2 d-flex flex-column justify-center">
<MudText Typo="Typo.h4" Align="Align.Center">My PCRBs</MudText>
<MudDivider DividerType="DividerType.Middle" Class="my-2" />
<MudTable Items="@myPCRBs"
Class="m-2"
Striped
SortLabel="Sort by"
Filter="new Func<PCRB, bool>(FilterFuncForPCRBTable)">
<ToolBarContent>
<MudSpacer />
<MudTextField @bind-Value="pcrbSearchString"
Placeholder="Search"
Adornment="Adornment.Start"
Immediate
AdornmentIcon="@Icons.Material.Filled.Search"
IconSize="Size.Medium"
Class="mt-0" />
</ToolBarContent>
<HeaderContent>
<MudTh>
<MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<PCRB,object>(x=>x.PlanNumber)">
PCRB#
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.Title)">
Title
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.CurrentStep)">
Current Step
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.InsertTimeStamp)">
Submitted Date
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.LastUpdateDate)">
Last Updated
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.ClosedDate)">
Completed Date
</MudTableSortLabel>
</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="PCRB#">
<MudLink OnClick="@(() => GoTo($"pcrb/{context.PlanNumber}"))">@context.PlanNumber</MudLink>
</MudTd>
<MudTd DataLabel="Title">@context.Title</MudTd>
<MudTd DataLabel="Current Step">@(GetCurrentPCRBStep(context.CurrentStep))</MudTd>
<MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.InsertTimeStamp)</MudTd>
<MudTd DataLabel="Last Updated">@DateTimeUtilities.GetDateAsStringMinDefault(context.LastUpdateDate)</MudTd>
<MudTd DataLabel="Completed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ClosedDate)</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager />
</PagerContent>
</MudTable>
</MudPaper>
} else {
<MudOverlay Visible DarkBackground="true" AutoClose="false">
<MudText Align="Align.Center" Typo="Typo.h3">Processing</MudText>
<MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" />
</MudOverlay>
}
</MudTabPanel>
</MudTabs>
</MudPaper>
@code {
private IEnumerable<Approval> approvalList = new List<Approval>();
private IEnumerable<MRB> myMRBs = new List<MRB>();
private IEnumerable<PCRB> myPCRBs = new List<PCRB>();
private IEnumerable<int> ecnNumbers = new HashSet<int>();
private IEnumerable<int> caNumbers = new HashSet<int>();
private IEnumerable<int> mrbNumbers = new HashSet<int>();
private IEnumerable<int> pcrbNumbers = new HashSet<int>();
private bool myApprovalsProcessing = false;
private bool myMrbsProcessing = false;
private bool myPcrbsProcessing = false;
private string mrbSearchString = "";
private string pcrbSearchString = "";
protected async override Task OnParametersSetAsync() {
try {
if (stateProvider.CurrentUser is not null) {
myApprovalsProcessing = true;
approvalList = (await approvalService.GetApprovalsForUserId(stateProvider.CurrentUser.UserID, true))
.Where(a => a.CompletedDate > DateTime.Now && a.ItemStatus == 0)
.ToList()
.OrderByDescending(x => x.AssignedDate);
myApprovalsProcessing = false;
myMrbsProcessing = true;
myMRBs = (await mrbService.GetAllMRBs(false)).Where(m => m.OriginatorID == stateProvider.CurrentUser.UserID)
.ToList()
.OrderByDescending(x => x.SubmittedDate);
myMrbsProcessing = false;
myPcrbsProcessing = true;
myPCRBs = (await pcrbService.GetAllPCRBs(false)).Where(p => p.OwnerID == stateProvider.CurrentUser.UserID)
.ToList()
.OrderByDescending(p => p.InsertTimeStamp);
myPcrbsProcessing = false;
}
} catch (Exception ex) {
myApprovalsProcessing = false;
myMrbsProcessing = false;
myPcrbsProcessing = false;
snackbar.Add($"Unable to load the dashboard, because {ex.Message}", Severity.Error);
}
}
private async Task FollowLink(int issueId) {
HashSet<Task> tasks = new();
bool isEcn = false;
bool isCa = false;
bool isMrb = false;
bool isPcrb = false;
if (ecnNumbers.Contains(issueId))
isEcn = true;
if (caNumbers.Contains(issueId))
isCa = true;
if (mrbNumbers.Contains(issueId))
isMrb = true;
if (pcrbNumbers.Contains(issueId))
isPcrb = true;
if (!isEcn && !isCa && !isMrb) {
Task<bool> isEcnTask = ecnService.ECNNumberIsValid(issueId);
tasks.Add(isEcnTask);
Task<bool> isCaTask = caService.CANumberIsValid(issueId);
tasks.Add(isCaTask);
Task<bool> isMrbTask = mrbService.NumberIsValid(issueId);
tasks.Add(isMrbTask);
Task<bool> isPcrbTask = pcrbService.IdIsValid(issueId);
tasks.Add(isPcrbTask);
await Task.WhenAll(tasks);
if (isEcnTask.Result) {
isEcn = true;
} else if (isCaTask.Result) {
isCa = true;
} else if (isMrbTask.Result) {
isMrb = true;
} else if (isPcrbTask.Result) {
isPcrb = true;
}
}
if (isEcn) await GoToExternal($"{Configuration["OldFabApprovalUrl"]}/ECN/Edit?IssueID={issueId}", "");
if (isCa) await GoToExternal($"{Configuration["OldFabApprovalUrl"]}/CorrectiveAction/Edit?IssueID={issueId}", "");
if (isMrb) GoTo($"mrb/{issueId}");
if (isPcrb) GoTo($"pcrb/{issueId}");
}
private void GoTo(string page) {
cache.Set("redirectUrl", page);
navigationManager.NavigateTo("/" + page);
}
private async Task GoToExternal(string url, string content) {
IJSObjectReference windowModule = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./js/OpenInNewWindow.js");
await windowModule.InvokeAsync<object>("OpenInNewWindow", url, content);
}
private bool FilterFuncForMRBTable(MRB mrb) => MRBFilterFunc(mrb, mrbSearchString);
private bool MRBFilterFunc(MRB mrb, string searchString) {
if (string.IsNullOrWhiteSpace(searchString))
return true;
if (mrb.Title.ToLower().Contains(searchString.Trim().ToLower()))
return true;
if (mrb.MRBNumber.ToString().Contains(searchString.Trim()))
return true;
return false;
}
private bool FilterFuncForPCRBTable(PCRB pcrb) => PCRBFilterFunc(pcrb, pcrbSearchString);
private bool PCRBFilterFunc(PCRB pcrb, string searchString) {
if (string.IsNullOrWhiteSpace(searchString))
return true;
if (pcrb.Title.ToLower().Contains(searchString.Trim().ToLower()))
return true;
if (pcrb.PlanNumber.ToString().Contains(searchString.Trim()))
return true;
return false;
}
private string GetCurrentPCRBStep(int step) {
if (step < 0 || step > (PCRB.Stages.Length - 1)) return string.Empty;
else return PCRB.Stages[step];
}
}

View File

@ -0,0 +1,113 @@
@page "/login"
@page "/login/{redirectUrl}"
@page "/login/{redirectUrl}/{redirectUrlSub}"
@attribute [AllowAnonymous]
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject NavigationManager navManager
@inject ISnackbar snackbar
<MudPaper Class="p-2 m-2">
<MudText Typo="Typo.h3" Align="Align.Center">Login</MudText>
</MudPaper>
<MudPaper Class="p-2 m-2">
<MudForm @bind-IsValid="@success" @bind-Errors="@errors">
<MudTextField T="string"
Label="Windows Username"
Required="true"
RequiredError="Username is required!"
Variant="Variant.Outlined"
@bind-Value=username
Class="m-1"
Immediate="true"
AutoFocus
OnKeyDown=SubmitIfEnter />
<MudTextField T="string"
Label="Windows Password"
Required="true"
RequiredError="Password is required!"
Variant="Variant.Outlined"
@bind-Value=password
InputType="InputType.Password"
Class="m-1"
Immediate="true"
OnKeyDown=SubmitIfEnter />
<MudButton
Variant="Variant.Filled"
Color="Color.Tertiary"
Disabled="@(!success)"
Class="m-1"
OnClick=SubmitLogin >
@if (processing) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Log In</MudText>
}
</MudButton>
<MudDivider />
@* <MudButton
Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m-1"
OnClick="LoginLocal" >
@if (processingLocal) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Log In (SSO)</MudText>
}
</MudButton> *@
</MudForm>
</MudPaper>
@code {
[Parameter]
public string? redirectUrl { get; set; }
[Parameter]
public string? redirectUrlSub { get; set; }
private bool success;
private bool processing = false;
private bool processingLocal = false;
private string[] errors = { };
private string? username;
private string? password;
private async Task SubmitLogin() {
processing = true;
if (string.IsNullOrWhiteSpace(username)) snackbar.Add("Username is required!", Severity.Error);
else if (string.IsNullOrWhiteSpace(password)) snackbar.Add("Password is required!", Severity.Error);
else {
await authStateProvider.LoginAsync(username, password);
if (!string.IsNullOrWhiteSpace(redirectUrl) && !string.IsNullOrWhiteSpace(redirectUrlSub)) {
navManager.NavigateTo($"{redirectUrl}/{redirectUrlSub}");
} else if (!string.IsNullOrWhiteSpace(redirectUrl)) {
navManager.NavigateTo(redirectUrl);
} else {
navManager.NavigateTo("dashboard");
}
}
processing = false;
}
private async Task SubmitIfEnter(KeyboardEventArgs e) {
if (e.Key == "Enter" && success) {
SubmitLogin();
}
}
private async Task LoginLocal() {
processingLocal = true;
await authStateProvider.LoginLocal();
if (!string.IsNullOrWhiteSpace(redirectUrl) && !string.IsNullOrWhiteSpace(redirectUrlSub)) {
navManager.NavigateTo($"{redirectUrl}/{redirectUrlSub}");
} else if (!string.IsNullOrWhiteSpace(redirectUrl)) {
navManager.NavigateTo(redirectUrl);
} else {
navManager.NavigateTo("dashboard");
}
processingLocal = false;
}
}

View File

@ -0,0 +1,119 @@
@page "/mrb/all"
@using System.Globalization
@inject IMRBService mrbService
@inject ISnackbar snackbar
@inject IMemoryCache cache
@inject NavigationManager navigationManager
<PageTitle>MRB</PageTitle>
<MudPaper Class="p-2 m-2">
<MudText Typo="Typo.h3" Align="Align.Center">MRB List</MudText>
</MudPaper>
@if (allMrbs is not null && allMrbs.Count() > 0) {
<MudTable Items="@allMrbs"
Class="m-2"
Striped="true"
Filter="new Func<MRB,bool>(FilterFuncForTable)"
SortLabel="Sort By"
Hover="true">
<ToolBarContent>
<MudSpacer />
<MudTextField @bind-Value="searchString"
Placeholder="Search"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search"
IconSize="Size.Medium"
Class="mt-0" />
</ToolBarContent>
<HeaderContent>
<MudTh>
<MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<MRB,object>(x=>x.MRBNumber)">
MRB#
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.Title)">
Title
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.OriginatorName)">
Originator
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.SubmittedDate)">
Submitted Date
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.ApprovalDate)">
Approval Date
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.CloseDate)">
Closed Date
</MudTableSortLabel>
</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="MRB#">
<MudLink OnClick="@(() => GoTo($"mrb/{context.MRBNumber}"))">@context.MRBNumber</MudLink>
</MudTd>
<MudTd DataLabel="Title">@context.Title</MudTd>
<MudTd DataLabel="Originator">@context.OriginatorName</MudTd>
<MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.SubmittedDate)</MudTd>
<MudTd DataLabel="Approval Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ApprovalDate)</MudTd>
<MudTd DataLabel="Closed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CloseDate)</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager />
</PagerContent>
</MudTable>
}
<MudOverlay @bind-Visible=inProcess DarkBackground="true" AutoClose="false">
<MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" />
</MudOverlay>
@code {
private bool inProcess = false;
private string searchString = "";
private IEnumerable<MRB> allMrbs = new List<MRB>();
protected override async Task OnParametersSetAsync() {
inProcess = true;
try {
if (mrbService is null) {
throw new Exception("MRB service not injected!");
} else {
allMrbs = await mrbService.GetAllMRBs(false);
}
} catch (Exception ex) {
snackbar.Add(ex.Message, Severity.Error);
}
inProcess = false;
}
private bool FilterFuncForTable(MRB mrb) => FilterFunc(mrb, searchString);
private bool FilterFunc(MRB mrb, string searchString) {
if (string.IsNullOrWhiteSpace(searchString))
return true;
if (mrb.Title.ToLower().Contains(searchString.Trim().ToLower()))
return true;
if (mrb.OriginatorName.ToLower().Contains(searchString.Trim().ToLower()))
return true;
if (mrb.MRBNumber.ToString().Contains(searchString.Trim()))
return true;
return false;
}
private void GoTo(string page) {
cache.Set("redirectUrl", page);
navigationManager.NavigateTo(page);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,126 @@
@page "/pcrb/all"
@using System.Globalization
@inject IPCRBService pcrbService
@inject ISnackbar snackbar
@inject IMemoryCache cache
@inject NavigationManager navigationManager
<PageTitle>PCRB</PageTitle>
<MudPaper Class="p-2 m-2">
<MudText Typo="Typo.h3" Align="Align.Center">PCRB List</MudText>
</MudPaper>
@if (allPCRBs is not null && allPCRBs.Count() > 0) {
<MudTable Items="@allPCRBs"
Class="m-2"
Striped="true"
Filter="new Func<PCRB,bool>(FilterFuncForTable)"
SortLabel="Sort By"
Hover="true">
<ToolBarContent>
<MudSpacer />
<MudTextField @bind-Value="searchString"
Placeholder="Search"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search"
IconSize="Size.Medium"
Class="mt-0" />
</ToolBarContent>
<HeaderContent>
<MudTh>
<MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<PCRB,object>(x=>x.PlanNumber)">
Change#
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.Title)">
Title
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.OwnerName)">
Owner
</MudTableSortLabel>
</MudTh>
<MudTh>Stage</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.InsertTimeStamp)">
Submitted Date
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.LastUpdateDate)">
Last Updated
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.ClosedDate)">
Closed Date
</MudTableSortLabel>
</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Plan#">
<MudLink OnClick="@(() => GoTo($"pcrb/{context.PlanNumber.ToString()}"))">@context.PlanNumber</MudLink>
</MudTd>
<MudTd DataLabel="Title">@context.Title</MudTd>
<MudTd DataLabel="Owner">@context.OwnerName</MudTd>
<MudTd DataLabel="Stage">@(GetStageName(context.CurrentStep))</MudTd>
<MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.InsertTimeStamp)</MudTd>
<MudTd DataLabel="Last Updated">@DateTimeUtilities.GetDateAsStringMinDefault(context.LastUpdateDate)</MudTd>
<MudTd DataLabel="Closed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ClosedDate)</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager />
</PagerContent>
</MudTable>
}
<MudOverlay @bind-Visible=inProcess DarkBackground="true" AutoClose="false">
<MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" />
</MudOverlay>
@code {
private bool inProcess = false;
private string searchString = "";
private IEnumerable<PCRB> allPCRBs = new List<PCRB>();
protected override async Task OnParametersSetAsync() {
inProcess = true;
try {
if (pcrbService is null) {
throw new Exception("PCRB service not injected!");
} else {
allPCRBs = await pcrbService.GetAllPCRBs(false);
}
} catch (Exception ex) {
snackbar.Add(ex.Message, Severity.Error);
}
inProcess = false;
}
private bool FilterFuncForTable(PCRB pcrb) => FilterFunc(pcrb, searchString);
private bool FilterFunc(PCRB pcrb, string searchString) {
if (string.IsNullOrWhiteSpace(searchString))
return true;
if (pcrb.Title.ToLower().Contains(searchString.Trim().ToLower()))
return true;
if (pcrb.OwnerName.ToLower().Contains(searchString.Trim().ToLower()))
return true;
if (pcrb.PlanNumber.ToString().Contains(searchString.Trim()))
return true;
return false;
}
private void GoTo(string page) {
cache.Set("redirectUrl", page);
navigationManager.NavigateTo(page);
}
private string GetStageName(int step) {
if (step >= PCRB.Stages.Length || step < 0) return "";
return PCRB.Stages[step];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
using MesaFabApproval.Client;
using MesaFabApproval.Client.Utilities;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MudBlazor.Services;
using MesaFabApproval.Client.Services;
using Microsoft.AspNetCore.Components.Authorization;
using System.Net.Http.Headers;
using MudBlazor;
WebAssemblyHostBuilder builder = WebAssemblyHostBuilder.CreateDefault(args);
string _apiBaseUrl = builder.Configuration["FabApprovalApiBaseUrl"] ??
throw new NullReferenceException("FabApprovalApiBaseUrl not found in config");
string _oldSiteUrl = builder.Configuration["OldFabApprovalUrl"] ??
throw new NullReferenceException("OldFabApprovalUrl not found in config");
builder.Services.AddTransient<ApiHttpClientHandler>();
builder.Services
.AddHttpClient("API_Handler", client => {
client.BaseAddress = new Uri(_apiBaseUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
});
builder.Services
.AddHttpClient("API", client => {
client.BaseAddress = new Uri(_apiBaseUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
})
.AddHttpMessageHandler<ApiHttpClientHandler>();
builder.Services
.AddHttpClient("OldSite", client => {
client.BaseAddress = new Uri(_oldSiteUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
});
builder.Services.AddMemoryCache();
builder.Services.AddMudServices(config => {
config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomCenter;
config.SnackbarConfiguration.PreventDuplicates = true;
config.SnackbarConfiguration.MaxDisplayedSnackbars = 5;
config.SnackbarConfiguration.SnackbarVariant = Variant.Filled;
config.SnackbarConfiguration.ShowCloseIcon = true;
config.SnackbarConfiguration.VisibleStateDuration = 7000;
config.SnackbarConfiguration.HideTransitionDuration = 500;
config.SnackbarConfiguration.ShowTransitionDuration = 500;
});
builder.Services.AddScoped<ILocalStorageService, LocalStorageService>();
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
builder.Services.AddScoped<ICustomerService, CustomerService>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IECNService, ECNService>();
builder.Services.AddScoped<ICAService, CAService>();
builder.Services.AddScoped<IPCRBService, PCRBService>();
builder.Services.AddScoped<IMRBService, MRBService>();
builder.Services.AddScoped<IApprovalService, ApprovalService>();
builder.Services.AddScoped<MesaFabApprovalAuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(sp =>
sp.GetRequiredService<MesaFabApprovalAuthStateProvider>());
builder.Services.AddAuthorizationCore();
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
await builder.Build().RunAsync();

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<DeleteExistingFiles>true</DeleteExistingFiles>
<ExcludeApp_Data>false</ExcludeApp_Data>
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>bin\Release\net8.0\browser-wasm\publish\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<SiteUrlToLaunchAfterPublish />
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<ProjectGuid>34d52f44-a81f-4247-8180-16e204824a07</ProjectGuid>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,300 @@
using System.Text;
using System.Text.Json;
using MesaFabApproval.Shared.Models;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.Client.Services;
public interface IApprovalService {
Task<int> GetRoleIdForRoleName(string roleName);
Task<IEnumerable<SubRole>> GetSubRolesForSubRoleName(string subRoleName, int roleId);
Task<IEnumerable<User>> GetApprovalGroupMembers(int subRoleId);
Task CreateApproval(Approval approval);
Task UpdateApproval(Approval approval);
Task Approve(Approval approval);
Task Deny(Approval approval);
Task<IEnumerable<Approval>> GetApprovalsForIssueId(int issueId, bool bypassCache);
Task<IEnumerable<Approval>> GetApprovalsForUserId(int userId, bool bypassCache);
}
public class ApprovalService : IApprovalService {
private readonly IMemoryCache _cache;
private readonly IHttpClientFactory _httpClientFactory;
public ApprovalService(IMemoryCache cache, IHttpClientFactory httpClientFactory) {
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_httpClientFactory = httpClientFactory ??
throw new ArgumentNullException("IHttpClientFactory not injected");
}
public async Task CreateApproval(Approval approval) {
if (approval is null) throw new ArgumentNullException("approval cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, "approval");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(approval),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) {
throw new Exception($"Unable to create approval, because {responseMessage.ReasonPhrase}");
}
await GetApprovalsForIssueId(approval.IssueID, true);
await GetApprovalsForUserId(approval.UserID, true);
}
public async Task<IEnumerable<Approval>> GetApprovalsForIssueId(int issueId, bool bypassCache) {
if (issueId <= 0) throw new ArgumentException($"{issueId} is not a valid issue ID");
IEnumerable<Approval>? approvals = null;
if (!bypassCache)
approvals = _cache.Get<IEnumerable<Approval>>($"approvals{issueId}");
if (approvals is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"approval/issue?issueId={issueId}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
approvals = JsonSerializer.Deserialize<IEnumerable<Approval>>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse approvals from API response");
_cache.Set($"approvals{issueId}", approvals, DateTimeOffset.Now.AddMinutes(15));
} else {
throw new Exception($"Unable to get approvals, because {responseMessage.ReasonPhrase}");
}
}
foreach (Approval approval in approvals) {
if (approval.ItemStatus < 0)
approval.StatusMessage = "Denied";
if (approval.ItemStatus == 0)
approval.StatusMessage = "Assigned";
if (approval.ItemStatus > 0)
approval.StatusMessage = "Approved";
}
return approvals;
}
public async Task<IEnumerable<Approval>> GetApprovalsForUserId(int userId, bool bypassCache) {
if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID");
IEnumerable<Approval>? approvals = null;
if (!bypassCache) approvals = _cache.Get<IEnumerable<Approval>>($"approvals{userId}");
if (approvals is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"approval/user?userId={userId}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
approvals = JsonSerializer.Deserialize<IEnumerable<Approval>>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse approvals from API response");
_cache.Set($"approvals{userId}", approvals, DateTimeOffset.Now.AddMinutes(15));
} else {
throw new Exception($"Unable to get approvals, because {responseMessage.ReasonPhrase}");
}
}
foreach (Approval approval in approvals) {
if (approval.ItemStatus < 0)
approval.StatusMessage = "Denied";
if (approval.ItemStatus == 0)
approval.StatusMessage = "Assigned";
if (approval.ItemStatus > 0)
approval.StatusMessage = "Approved";
}
return approvals;
}
public async Task UpdateApproval(Approval approval) {
if (approval is null) throw new ArgumentNullException("approval cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, "approval");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(approval),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) {
throw new Exception($"Unable to update approval, because {responseMessage.ReasonPhrase}");
}
await GetApprovalsForIssueId(approval.IssueID, true);
await GetApprovalsForUserId(approval.UserID, true);
}
public async Task Approve(Approval approval) {
if (approval is null) throw new ArgumentNullException("approval cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, "approval/approve");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(approval),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) {
throw new Exception($"Approval failed, because {responseMessage.ReasonPhrase}");
}
await GetApprovalsForIssueId(approval.IssueID, true);
await GetApprovalsForUserId(approval.UserID, true);
}
public async Task Deny(Approval approval) {
if (approval is null) throw new ArgumentNullException("approval cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, "approval/deny");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(approval),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) {
throw new Exception($"Denial failed, because {responseMessage.ReasonPhrase}");
}
}
public async Task<int> GetRoleIdForRoleName(string roleName) {
if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("role name cannot be null or empty");
int roleId = _cache.Get<int>($"roleId{roleName}");
if (roleId <= 0) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"approval/roleId?roleName={roleName}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
roleId = JsonSerializer.Deserialize<int>(responseContent, jsonSerializerOptions);
if (roleId <= 0)
throw new Exception($"unable to find role ID for {roleName}");
_cache.Set($"roleId{roleName}", roleId, DateTimeOffset.Now.AddMinutes(15));
} else {
throw new Exception($"Unable to get role ID, because {responseMessage.ReasonPhrase}");
}
}
return roleId;
}
public async Task<IEnumerable<SubRole>> GetSubRolesForSubRoleName(string subRoleName, int roleId) {
if (string.IsNullOrWhiteSpace(subRoleName)) throw new ArgumentException("role name cannot be null or empty");
if (roleId <= 0) throw new ArgumentException($"{roleId} is not a valid role ID");
IEnumerable<SubRole>? subRoles = _cache.Get<IEnumerable<SubRole>>($"subRoles{subRoleName}");
if (subRoles is null || subRoles.Count() <= 0) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"approval/subRoles?subRoleName={subRoleName}&roleId={roleId}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
subRoles = JsonSerializer.Deserialize<IEnumerable<SubRole>>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse sub roles from API response");
if (subRoles is not null && subRoles.Count() > 0) {
_cache.Set($"subRoles{subRoleName}", subRoles, DateTimeOffset.Now.AddMinutes(15));
} else {
throw new Exception($"unable to find sub roles for {subRoleName}");
}
} else {
throw new Exception($"Unable to get sub roles, because {responseMessage.ReasonPhrase}");
}
}
return subRoles;
}
public async Task<IEnumerable<User>> GetApprovalGroupMembers(int subRoleId) {
if (subRoleId <= 0) throw new ArgumentException($"{subRoleId} is not a valid sub role ID");
IEnumerable<User>? members = _cache.Get<IEnumerable<User>>($"approvalMembers{subRoleId}");
if (members is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"approval/members?subRoleId={subRoleId}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
members = JsonSerializer.Deserialize<IEnumerable<User>>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse users from API response");
if (members is null || members.Count() <= 0)
throw new Exception($"unable to find group members for sub role {subRoleId}");
_cache.Set($"approvalMembers{subRoleId}", members, DateTimeOffset.Now.AddMinutes(2));
} else {
throw new Exception($"Unable to get group members, because {responseMessage.ReasonPhrase}");
}
}
return members;
}
}

View File

@ -0,0 +1,232 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Text.Json;
using MesaFabApproval.Shared.Models;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.Client.Services;
public interface IAuthenticationService {
Task<ClaimsPrincipal> SendAuthenticationRequest(string loginId, string password);
Task<ClaimsPrincipal> AttemptLocalUserAuth();
Task<ClaimsPrincipal> FetchAuthState();
Task ClearTokens();
Task ClearCurrentUser();
Task SetTokens(string jwt, string refreshToken);
Task SetLoginId(string loginId);
Task SetCurrentUser(User user);
Task<User> GetCurrentUser();
Task<AuthTokens> GetAuthTokens();
Task<string> GetLoginId();
ClaimsPrincipal GetClaimsPrincipalFromJwt(string jwt);
}
public class AuthenticationService : IAuthenticationService {
private readonly ILocalStorageService _localStorageService;
private readonly IMemoryCache _cache;
private readonly IHttpClientFactory _httpClientFactory;
public AuthenticationService(ILocalStorageService localStorageService,
IMemoryCache cache,
IHttpClientFactory httpClientFactory) {
_localStorageService = localStorageService ??
throw new ArgumentNullException("ILocalStorageService not injected");
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException("IHttpClientFactory not injected");
}
public async Task<ClaimsPrincipal> SendAuthenticationRequest(string loginId, string password) {
if (string.IsNullOrWhiteSpace(loginId)) throw new ArgumentException("loginId cannot be null or empty");
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("password cannot be null or empty");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
AuthAttempt authAttempt = new() {
LoginID = loginId,
Password = password
};
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "auth/login");
request.Content = new StringContent(JsonSerializer.Serialize(authAttempt),
Encoding.UTF8,
"application/json");
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
if (httpResponseMessage.IsSuccessStatusCode) {
string responseContent = await httpResponseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
LoginResult loginResult = JsonSerializer.Deserialize<LoginResult>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse login result from API response");
if (!loginResult.IsAuthenticated) throw new Exception($"User with Login ID {loginId} not authorized");
await SetLoginId(loginId);
await SetTokens(loginResult.AuthTokens.JwtToken, loginResult.AuthTokens.RefreshToken);
await SetCurrentUser(loginResult.User);
ClaimsPrincipal principal = GetClaimsPrincipalFromJwt(loginResult.AuthTokens.JwtToken);
return principal;
} else {
throw new Exception($"Login API request failed for {loginId}, because {httpResponseMessage.ReasonPhrase}");
}
}
public async Task<ClaimsPrincipal> AttemptLocalUserAuth() {
WindowsIdentity? user = WindowsIdentity.GetCurrent();
if (user is null)
throw new Exception("no authenticated user found");
if (!user.IsAuthenticated)
throw new Exception("you are not authenticated");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "auth/login/localWindows");
request.Content = new StringContent(JsonSerializer.Serialize(user),
Encoding.UTF8,
"application/json");
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
if (httpResponseMessage.IsSuccessStatusCode) {
string responseContent = await httpResponseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
LoginResult loginResult = JsonSerializer.Deserialize<LoginResult>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse login result from API response");
if (!loginResult.IsAuthenticated) throw new Exception($"User with Login ID {user.Name} not authorized");
await SetLoginId(loginResult.User.LoginID);
await SetTokens(loginResult.AuthTokens.JwtToken, loginResult.AuthTokens.RefreshToken);
await SetCurrentUser(loginResult.User);
ClaimsPrincipal principal = GetClaimsPrincipalFromJwt(loginResult.AuthTokens.JwtToken);
return principal;
} else {
throw new Exception($"Login API request failed for {user.Name}, because {httpResponseMessage.ReasonPhrase}");
}
}
public async Task<ClaimsPrincipal> FetchAuthState() {
string? jwt = _cache.Get<string>("MesaFabApprovalJwt");
if (jwt is null) jwt = await _localStorageService.GetItem("MesaFabApprovalJwt");
if (jwt is null) throw new Exception("Unable to find JWT");
ClaimsPrincipal principal = GetClaimsPrincipalFromJwt(jwt);
return principal;
}
public async Task ClearTokens() {
_cache.Remove("MesaFabApprovalJwt");
await _localStorageService.RemoveItem("MesaFabApprovalJwt");
_cache.Remove("MesaFabApprovalRefreshToken");
await _localStorageService.RemoveItem("MesaFabApprovalRefreshToken");
}
public async Task SetTokens(string jwt, string refreshToken) {
if (string.IsNullOrWhiteSpace(jwt)) throw new ArgumentNullException("JWT cannot be null or empty");
if (string.IsNullOrWhiteSpace(refreshToken)) throw new ArgumentNullException("Refresh token cannot be null or empty");
_cache.Set<string>("MesaFabApprovalJwt", jwt);
await _localStorageService.AddItem("MesaFabApprovalJwt", jwt);
_cache.Set<string>("MesaFabApprovalRefreshToken", refreshToken);
await _localStorageService.AddItem("MesaFabApprovalRefreshToken", refreshToken);
}
public async Task SetLoginId(string loginId) {
if (string.IsNullOrWhiteSpace(loginId)) throw new ArgumentNullException("LoginId cannot be null or empty");
_cache.Set<string>("MesaFabApprovalUserId", loginId);
await _localStorageService.AddItem("MesaFabApprovalUserId", loginId);
}
public async Task SetCurrentUser(User? user) {
_cache.Set<User>("MesaFabApprovalCurrentUser", user);
await _localStorageService.AddItem<User>("MesaFabApprovalCurrentUser", user);
}
public async Task ClearCurrentUser() {
_cache.Remove("MesaFabApprovalCurrentUser");
await _localStorageService.RemoveItem("MesaFabApprovalCurrentUser");
_cache.Remove("MesaFabApprovalUserId");
await _localStorageService.RemoveItem("MesaFabApprovalUserId");
}
public async Task<User> GetCurrentUser() {
User? currentUser = null;
currentUser = _cache.Get<User>("MesaFabApprovalCurrentUser");
if (currentUser is null)
currentUser = await _localStorageService.GetItem<User>("MesaFabApprovalCurrentUser");
return currentUser;
}
public async Task<AuthTokens> GetAuthTokens() {
AuthTokens? authTokens = null;
string? jwt = _cache.Get<string>("MesaFabApprovalJwt");
if (jwt is null) jwt = await _localStorageService.GetItem("MesaFabApprovalJwt");
string? refreshToken = _cache.Get<string>("MesaFabApprovalRefreshToken");
if (refreshToken is null) refreshToken = await _localStorageService.GetItem("MesaFabApprovalRefreshToken");
if (!string.IsNullOrWhiteSpace(jwt) && !string.IsNullOrWhiteSpace(refreshToken)) {
authTokens = new() {
JwtToken = jwt,
RefreshToken = refreshToken
};
}
return authTokens;
}
public async Task<string> GetLoginId() {
string? loginId = _cache.Get<string>("MesaFabApprovalUserId");
if (loginId is null) loginId = await _localStorageService.GetItem("MesaFabApprovalUserId");
return loginId;
}
public ClaimsPrincipal GetClaimsPrincipalFromJwt(string jwt) {
if (string.IsNullOrWhiteSpace(jwt)) throw new ArgumentException("JWT cannot be null or empty");
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
if (!tokenHandler.CanReadToken(jwt)) {
throw new Exception("Unable to parse JWT from API");
}
JwtSecurityToken jwtSecurityToken = tokenHandler.ReadJwtToken(jwt);
ClaimsIdentity identity = new ClaimsIdentity(jwtSecurityToken.Claims, "MesaFabApprovalWasm");
return new(identity);
}
}

View File

@ -0,0 +1,44 @@
using System.Text.Json;
namespace MesaFabApproval.Client.Services;
public interface ICAService {
Task<bool> CANumberIsValid(int number);
}
public class CAService : ICAService {
private readonly IHttpClientFactory _httpClientFactory;
public CAService(IHttpClientFactory httpClientFactory) {
_httpClientFactory = httpClientFactory ??
throw new ArgumentNullException("IHttpClientFactory not injected");
}
public async Task<bool> CANumberIsValid(int number) {
if (number <= 0) return false;
try {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"ca/isValidCANumber?number={number}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
bool isValid = JsonSerializer.Deserialize<bool>(responseContent, jsonSerializerOptions);
return isValid;
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
} catch (Exception ex) {
throw new Exception($"Unable to determine if {number} is a valid CA#, because {ex.Message}");
}
}
}

View File

@ -0,0 +1,51 @@
using System.Text.Json;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.Client.Services;
public interface ICustomerService {
Task<IEnumerable<string>> GetAllCustomerNames();
}
public class CustomerService : ICustomerService {
private readonly IMemoryCache _cache;
private readonly IHttpClientFactory _httpClientFactory;
public CustomerService(IMemoryCache cache, IHttpClientFactory httpClientFactory) {
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_httpClientFactory = httpClientFactory ??
throw new ArgumentNullException("IHttpClientFactory not injected");
}
public async Task<IEnumerable<string>> GetAllCustomerNames() {
IEnumerable<string>? allCustomerNames = null;
allCustomerNames = _cache.Get<IEnumerable<string>>("allCustomerNames");
if (allCustomerNames is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"customer/all");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
allCustomerNames = JsonSerializer.Deserialize<IEnumerable<string>>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse names from API response");
_cache.Set($"allCustomerNames", allCustomerNames, DateTimeOffset.Now.AddHours(1));
} else {
throw new Exception($"Unable to get all customer names, because {responseMessage.ReasonPhrase}");
}
}
return allCustomerNames;
}
}

View File

@ -0,0 +1,52 @@
using System.Text.Json;
namespace MesaFabApproval.Client.Services;
public interface IECNService {
Task<string> ECNNumberIsValidStr(int ecnNumber);
Task<bool> ECNNumberIsValid(int number);
}
public class ECNService : IECNService {
private readonly IHttpClientFactory _httpClientFactory;
public ECNService(IHttpClientFactory httpClientFactory) {
_httpClientFactory = httpClientFactory ??
throw new ArgumentNullException("IHttpClientFactory not injected");
}
public async Task<string> ECNNumberIsValidStr(int ecnNumber) {
if (ecnNumber <= 0 || !await ECNNumberIsValid(ecnNumber))
return $"{ecnNumber} is not a valid ECN#";
return null;
}
public async Task<bool> ECNNumberIsValid(int number) {
if (number <= 0) return false;
try {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"ecn/isValidEcnNumber?number={number}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
bool isValid = JsonSerializer.Deserialize<bool>(responseContent, jsonSerializerOptions);
return isValid;
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
} catch (Exception ex) {
throw new Exception($"Unable to determine if {number} is a valid ECN#, because {ex.Message}");
}
}
}

View File

@ -0,0 +1,49 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.JSInterop;
namespace MesaFabApproval.Client.Services;
public interface ILocalStorageService {
Task AddItem(string key, string value);
Task AddItem<T>(string key, T value);
Task RemoveItem(string key);
Task<string> GetItem(string key);
Task<T?> GetItem<T>(string key);
}
public class LocalStorageService : ILocalStorageService {
private readonly IJSRuntime _jsRuntime;
public LocalStorageService(IJSRuntime jsRuntime) {
_jsRuntime = jsRuntime;
}
public async Task AddItem(string key, string value) {
await _jsRuntime.InvokeVoidAsync("localStorage.setItem", key, value);
}
public async Task AddItem<T>(string key, T value) {
string item = JsonSerializer.Serialize(value);
await _jsRuntime.InvokeVoidAsync("localStorage.setItem", key, item);
}
public async Task RemoveItem(string key) {
await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", key);
}
public async Task<string> GetItem(string key) {
return await _jsRuntime.InvokeAsync<string>("localStorage.getItem", key);
}
public async Task<T?> GetItem<T>(string key) {
string item = await _jsRuntime.InvokeAsync<string>("localStorage.getItem", key);
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<T>(item, jsonSerializerOptions);
}
}

View File

@ -0,0 +1,755 @@
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Utilities;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Caching.Memory;
using MudBlazor;
namespace MesaFabApproval.Client.Services;
public interface IMRBService {
Task<IEnumerable<MRB>> GetAllMRBs(bool bypassCache);
Task<MRB> GetMRBById(int id, bool bypassCache = false);
Task<MRB> GetMRBByTitle(string title, bool bypassCache);
Task CreateNewMRB(MRB mrb);
Task RecallMRB(MRB mrb, User recallUser);
Task DeleteMRB(int mrbNumber);
Task UpdateMRB(MRB mrb);
Task SubmitForApproval(MRB mrb);
Task GenerateActionTasks(MRB mrb, MRBAction action);
Task CreateMRBAction(MRBAction mrbAction);
Task<IEnumerable<MRBAction>> GetMRBActionsForMRB(int mrbNumber, bool bypassCache);
Task UpdateMRBAction(MRBAction mrbAction);
Task DeleteMRBAction(MRBAction mrbAction);
Task UploadAttachments(IEnumerable<IBrowserFile> files, int mrbNumber);
Task UploadActionAttachments(IEnumerable<IBrowserFile> files, int actionId);
Task<IEnumerable<MRBAttachment>> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache);
Task<IEnumerable<MRBActionAttachment>> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache);
Task DeleteAttachment(MRBAttachment attachment);
Task NotifyNewApprovals(MRB mrb);
Task NotifyApprovers(MRBNotification notification);
Task NotifyOriginator(MRBNotification notification);
Task NotifyQAPreApprover(MRBNotification notification);
Task<bool> NumberIsValid(int number);
}
public class MRBService : IMRBService {
private readonly IMemoryCache _cache;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ISnackbar _snackbar;
private readonly IUserService _userService;
private readonly IApprovalService _approvalService;
public MRBService(IMemoryCache cache,
IHttpClientFactory httpClientFactory,
ISnackbar snackbar,
IUserService userService,
IApprovalService approvalService) {
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException("IHttpClientFactory not injected");
_snackbar = snackbar ?? throw new ArgumentNullException("ISnackbar not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
_approvalService = approvalService ?? throw new ArgumentNullException("IApprovalService not injected");
}
public async Task CreateNewMRB(MRB mrb) {
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, "mrb/new");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(mrb),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) {
throw new Exception($"Unable to generate new MRB, because {responseMessage.ReasonPhrase}");
}
mrb = await GetMRBByTitle(mrb.Title, true);
_cache.Set($"mrb{mrb.MRBNumber}", mrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"mrb{mrb.Title}", mrb, DateTimeOffset.Now.AddHours(1));
IEnumerable<MRB>? allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs");
if (allMrbs is not null) {
List<MRB> mrbList = allMrbs.ToList();
mrbList.Add(mrb);
_cache.Set("allMrbs", mrbList);
}
}
public async Task RecallMRB(MRB mrb, User recallUser) {
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
if (mrb.StageNo < 1) throw new ArgumentException("MRB already in Draft stage");
if (mrb.StageNo >= 4) throw new Exception("you cannot recall a completed MRB");
mrb.StageNo = 0;
mrb.SubmittedDate = DateTimeUtilities.MIN_DT;
mrb.ApprovalDate = DateTimeUtilities.MAX_DT;
mrb.CloseDate = DateTimeUtilities.MAX_DT;
await UpdateMRB(mrb);
IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(mrb.MRBNumber, false);
foreach (Approval approval in approvals) {
if (approval.CompletedDate >= DateTimeUtilities.MAX_DT) {
string comment = $"Recalled by {recallUser.GetFullName()}.";
approval.Comments = comment;
approval.CompletedDate = DateTime.Now;
approval.ItemStatus = -1;
await _approvalService.UpdateApproval(approval);
}
}
string message = $"MRB# {mrb.MRBNumber} [{mrb.Title}] has been recalled by {recallUser.GetFullName()}.";
MRBNotification notification = new() { Message = message, MRB = mrb };
await NotifyApprovers(notification);
}
public async Task DeleteMRB(int mrbNumber) {
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"mrb/delete?mrbNumber={mrbNumber}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) {
throw new Exception($"Unable to delete MRB# {mrbNumber}, because {responseMessage.ReasonPhrase}");
}
IEnumerable<MRB> allMRBs = await GetAllMRBs(true);
_cache.Set("allMrbs", allMRBs);
}
public async Task<IEnumerable<MRB>> GetAllMRBs(bool bypassCache) {
try {
IEnumerable<MRB>? allMRBs = null;
if (!bypassCache) allMRBs = _cache.Get<IEnumerable<MRB>>("allMrbs");
if (allMRBs is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrb/all?bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
allMRBs = JsonSerializer.Deserialize<IEnumerable<MRB>>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse MRBs from API response");
_cache.Set($"allMrbs", allMRBs, DateTimeOffset.Now.AddMinutes(15));
} else {
throw new Exception($"Unable to get all MRBs, because {responseMessage.ReasonPhrase}");
}
}
return allMRBs;
} catch (Exception) {
throw;
}
}
public async Task<bool> NumberIsValid(int number) {
try {
if (number <= 0) return false;
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrb/numberIsValid?number={number}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
bool isValid = JsonSerializer.Deserialize<bool>(responseContent, jsonSerializerOptions);
return isValid;
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
} catch (Exception ex) {
throw new Exception($"Unable to determine if {number} is a valid MRB#, because {ex.Message}");
}
}
public async Task<MRB> GetMRBById(int id, bool bypassCache=false) {
if (id <= 0) throw new ArgumentException($"Invalid MRB number: {id}");
MRB? mrb = null;
if (!bypassCache)
mrb = _cache.Get<MRB>($"mrb{id}");
if (mrb is null && !bypassCache) mrb = _cache.Get<IEnumerable<MRB>>("allMrbs")?.FirstOrDefault(m => m.MRBNumber == id);
if (mrb is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrb/getById?id={id}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
mrb = JsonSerializer.Deserialize<MRB>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse MRB from API response");
_cache.Set($"mrb{mrb.MRBNumber}", mrb, DateTimeOffset.Now.AddHours(1));
} else {
throw new Exception($"Unable to get MRB by Id, because {responseMessage.ReasonPhrase}");
}
}
return mrb;
}
public async Task<MRB> GetMRBByTitle(string title, bool bypassCache) {
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty");
MRB? mrb = null;
if (!bypassCache) mrb = _cache.Get<MRB>($"mrb{title}");
if (mrb is null && !bypassCache)
mrb = _cache.Get<IEnumerable<MRB>>("allMrbs")?.FirstOrDefault(m => m.Title.Equals(title));
if (mrb is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrb/getByTitle?title={title}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
mrb = JsonSerializer.Deserialize<MRB>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse MRB from API response");
_cache.Set($"mrb{mrb.Title}", mrb, DateTimeOffset.Now.AddHours(1));
} else {
throw new Exception($"Unable to get MRB by title, because {responseMessage.ReasonPhrase}");
}
}
return mrb;
}
public async Task UpdateMRB(MRB mrb) {
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, $"mrb");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(mrb),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) {
throw new Exception($"Unable to update MRB, because {responseMessage.ReasonPhrase}");
}
_cache.Set($"mrb{mrb.MRBNumber}", mrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"mrb{mrb.Title}", mrb, DateTimeOffset.Now.AddHours(1));
IEnumerable<MRB>? allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs");
if (allMrbs is not null) {
List<MRB> mrbList = allMrbs.ToList();
mrbList.RemoveAll(m => m.MRBNumber == mrb.MRBNumber);
mrbList.Add(mrb);
_cache.Set("allMrbs", mrbList);
}
}
public async Task CreateMRBAction(MRBAction mrbAction) {
if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, "mrbAction");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(mrbAction),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to create new MRB action, because {responseMessage.ReasonPhrase}");
await GetMRBActionsForMRB(mrbAction.MRBNumber, true);
}
public async Task<IEnumerable<MRBAction>> GetMRBActionsForMRB(int mrbNumber, bool bypassCache) {
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#");
IEnumerable<MRBAction>? mrbActions = null;
if (!bypassCache)
mrbActions = _cache.Get<IEnumerable<MRBAction>>($"mrbActions{mrbNumber}");
if (mrbActions is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrbAction?mrbNumber={mrbNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
mrbActions = JsonSerializer.Deserialize<IEnumerable<MRBAction>>(responseContent, jsonSerializerOptions) ??
new List<MRBAction>();
if (mrbActions.Count() > 0)
_cache.Set($"mrbActions{mrbNumber}", mrbActions, DateTimeOffset.Now.AddMinutes(5));
} else {
throw new Exception($"Unable to get MRB {mrbNumber} actions, because {responseMessage.ReasonPhrase}");
}
}
return mrbActions;
}
public async Task UpdateMRBAction(MRBAction mrbAction) {
if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, $"mrbAction");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(mrbAction),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) {
throw new Exception($"Unable to update MRB action, because {responseMessage.ReasonPhrase}");
}
IEnumerable<MRBAction>? mrbActions = _cache.Get<IEnumerable<MRBAction>>($"mrbActions{mrbAction.MRBNumber}");
if (mrbActions is not null) {
List<MRBAction> mrbActionList = mrbActions.ToList();
mrbActionList.RemoveAll(a => a.ActionID == mrbAction.ActionID);
mrbActionList.Add(mrbAction);
_cache.Set($"mrbActions{mrbAction.MRBNumber}", mrbActionList, DateTimeOffset.Now.AddMinutes(5));
}
}
public async Task DeleteMRBAction(MRBAction mrbAction) {
if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null");
if (mrbAction.ActionID <= 0) throw new ArgumentException($"{mrbAction.ActionID} is not a valid MRBActionID");
if (mrbAction.MRBNumber <= 0) throw new ArgumentException($"{mrbAction.MRBNumber} is not a valid MRBNumber");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
string route = $"mrbAction?mrbActionID={mrbAction.ActionID}&mrbNumber={mrbAction.MRBNumber}";
HttpRequestMessage requestMessage = new(HttpMethod.Delete, route);
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to delete MRB action {mrbAction.ActionID}");
IEnumerable<MRBAction>? mrbActions = _cache.Get<IEnumerable<MRBAction>>($"mrbActions{mrbAction.MRBNumber}");
if (mrbActions is not null) {
List<MRBAction> mrbActionList = mrbActions.ToList();
mrbActionList.RemoveAll(a => a.ActionID == mrbAction.ActionID);
_cache.Set($"mrbActions{mrbAction.MRBNumber}", mrbActionList, DateTimeOffset.Now.AddMinutes(5));
}
}
public async Task UploadAttachments(IEnumerable<IBrowserFile> files, int mrbNumber) {
if (files is null) throw new ArgumentNullException("Files cannot be null");
if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty");
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"mrb/attach?mrbNumber={mrbNumber}");
using MultipartFormDataContent content = new MultipartFormDataContent();
foreach (IBrowserFile file in files) {
try {
long maxFileSize = 1024L * 1024L * 1024L * 2L;
StreamContent fileContent = new StreamContent(file.OpenReadStream(maxFileSize));
FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();
const string defaultContentType = "application/octet-stream";
if (!contentTypeProvider.TryGetContentType(file.Name, out string? contentType)) {
contentType = defaultContentType;
}
fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
content.Add(content: fileContent, name: "\"files\"", fileName: file.Name);
} catch (Exception ex) {
_snackbar.Add($"File {file.Name} not saved, because {ex.Message}");
}
}
requestMessage.Content = content;
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to save attachments, because {responseMessage.ReasonPhrase}");
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
IEnumerable<UploadResult> results = JsonSerializer.Deserialize<IEnumerable<UploadResult>>(responseContent, jsonSerializerOptions) ??
new List<UploadResult>();
foreach (UploadResult result in results) {
if (result.UploadSuccessful) {
_snackbar.Add($"{result.FileName} successfully uploaded", Severity.Success);
} else {
_snackbar.Add($"{result.FileName} not uploaded, because {result.Error}", Severity.Error);
}
}
await GetAllAttachmentsForMRB(mrbNumber, true);
}
public async Task UploadActionAttachments(IEnumerable<IBrowserFile> files, int actionId) {
if (files is null) throw new ArgumentNullException("Files cannot be null");
if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty");
if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"mrb/action/attach?actionId={actionId}");
using MultipartFormDataContent content = new MultipartFormDataContent();
foreach (IBrowserFile file in files) {
try {
long maxFileSize = 1024L * 1024L * 1024L * 2L;
StreamContent fileContent = new StreamContent(file.OpenReadStream(maxFileSize));
FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();
const string defaultContentType = "application/octet-stream";
if (!contentTypeProvider.TryGetContentType(file.Name, out string? contentType)) {
contentType = defaultContentType;
}
fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
content.Add(content: fileContent, name: "\"files\"", fileName: file.Name);
} catch (Exception ex) {
_snackbar.Add($"File {file.Name} not saved, because {ex.Message}");
}
}
requestMessage.Content = content;
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to save action attachments, because {responseMessage.ReasonPhrase}");
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
IEnumerable<UploadResult> results = JsonSerializer.Deserialize<IEnumerable<UploadResult>>(responseContent, jsonSerializerOptions) ??
new List<UploadResult>();
foreach (UploadResult result in results) {
if (result.UploadSuccessful) {
_snackbar.Add($"{result.FileName} successfully uploaded", Severity.Success);
} else {
_snackbar.Add($"{result.FileName} not uploaded, because {result.Error}", Severity.Error);
}
}
await GetAllAttachmentsForMRB(actionId, true);
}
public async Task<IEnumerable<MRBAttachment>> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache) {
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#");
IEnumerable<MRBAttachment>? mrbAttachments = null;
if (!bypassCache)
mrbAttachments = _cache.Get<IEnumerable<MRBAttachment>>($"mrbAttachments{mrbNumber}");
if (mrbAttachments is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrb/attachments?mrbNumber={mrbNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
mrbAttachments = JsonSerializer.Deserialize<IEnumerable<MRBAttachment>>(responseContent, jsonSerializerOptions) ??
new List<MRBAttachment>();
if (mrbAttachments.Count() > 0)
_cache.Set($"mrbAttachments{mrbNumber}", mrbAttachments, DateTimeOffset.Now.AddMinutes(5));
} else {
throw new Exception($"Unable to get MRB {mrbNumber} attachments, because {responseMessage.ReasonPhrase}");
}
}
return mrbAttachments;
}
public async Task<IEnumerable<MRBActionAttachment>> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache) {
if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#");
IEnumerable<MRBActionAttachment>? actionAttachments = null;
if (!bypassCache)
actionAttachments = _cache.Get<IEnumerable<MRBActionAttachment>>($"mrbActionAttachments{mrbNumber}");
if (actionAttachments is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrb/action/attachments?mrbNumber={mrbNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
actionAttachments = JsonSerializer.Deserialize<IEnumerable<MRBActionAttachment>>(responseContent, jsonSerializerOptions) ??
new List<MRBActionAttachment>();
if (actionAttachments.Count() > 0)
_cache.Set($"mrbActionAttachments{mrbNumber}", actionAttachments, DateTimeOffset.Now.AddMinutes(5));
} else {
throw new Exception($"Unable to get MRB {mrbNumber} action attachments, because {responseMessage.ReasonPhrase}");
}
}
return actionAttachments;
}
public async Task DeleteAttachment(MRBAttachment attachment) {
if (attachment is null) throw new ArgumentNullException("MRB attachment cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Delete, "mrb/attach");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(attachment),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to delete MRB attachment");
IEnumerable<MRBAttachment>? mrbAttachments = _cache.Get<IEnumerable<MRBAttachment>>($"mrbAttachments{attachment.MRBNumber}");
if (mrbAttachments is not null) {
List<MRBAttachment> mrbAttachmentList = mrbAttachments.ToList();
mrbAttachmentList.RemoveAll(a => a.AttachmentID == attachment.AttachmentID);
_cache.Set($"mrbAttachments{attachment.MRBNumber}", mrbAttachmentList, DateTimeOffset.Now.AddMinutes(5));
}
}
public async Task SubmitForApproval(MRB mrb) {
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
string roleName = "QA_PRE_APPROVAL";
string subRoleName = "QA_PRE_APPROVAL";
if (mrb.StageNo > 1) {
roleName = "MRB Approver";
subRoleName = "MRBApprover";
}
int roleId = await _approvalService.GetRoleIdForRoleName(roleName);
if (roleId <= 0) throw new Exception($"could not find {roleName} role ID");
IEnumerable<SubRole> subRoles = await _approvalService.GetSubRolesForSubRoleName(subRoleName, roleId);
foreach (SubRole subRole in subRoles) {
IEnumerable<User> members = await _approvalService.GetApprovalGroupMembers(subRole.SubRoleID);
foreach (User member in members) {
Approval approval = new() {
IssueID = mrb.MRBNumber,
RoleName = roleName,
SubRole = subRole.SubRoleName,
UserID = member.UserID,
SubRoleID = subRole.SubRoleID,
AssignedDate = DateTime.Now,
Step = mrb.StageNo
};
await _approvalService.CreateApproval(approval);
}
}
}
public async Task GenerateActionTasks(MRB mrb, MRBAction action) {
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
if (action is null) throw new ArgumentNullException("MRBAction cannot be null");
string roleName = "MRB Actions";
string subRoleName = "MRBActions";
int roleId = await _approvalService.GetRoleIdForRoleName(roleName);
if (roleId <= 0) throw new Exception($"could not find {roleName} role ID");
IEnumerable<SubRole> subRoles = await _approvalService.GetSubRolesForSubRoleName(subRoleName, roleId);
foreach (SubRole subRole in subRoles) {
IEnumerable<User> members = await _approvalService.GetApprovalGroupMembers(subRole.SubRoleID);
foreach (User member in members) {
Approval approval = new() {
IssueID = action.MRBNumber,
RoleName = roleName,
SubRole = subRole.SubRoleName,
UserID = member.UserID,
SubRoleID = subRole.SubRoleID,
AssignedDate = DateTime.Now,
Step = mrb.StageNo,
SubRoleCategoryItem = subRole.SubRoleCategoryItem,
TaskID = action.ActionID
};
await _approvalService.CreateApproval(approval);
}
}
}
public async Task NotifyNewApprovals(MRB mrb) {
if (mrb is null) throw new ArgumentNullException("MRB cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"mrb/notify/new-approvals");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(mrb),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to notify new MRB approvers, because {responseMessage.ReasonPhrase}");
}
public async Task NotifyApprovers(MRBNotification notification) {
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"mrb/notify/approvers");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(notification),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to notify MRB approvers, because {responseMessage.ReasonPhrase}");
}
public async Task NotifyOriginator(MRBNotification notification) {
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"mrb/notify/originator");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(notification),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to notify MRB originator, because {responseMessage.ReasonPhrase}");
}
public async Task NotifyQAPreApprover(MRBNotification notification) {
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"mrb/notify/qa-pre-approver");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(notification),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to notify QA pre approver, because {responseMessage.ReasonPhrase}");
}
}

View File

@ -0,0 +1,111 @@
using System.Security.Claims;
using MesaFabApproval.Shared.Models;
using Microsoft.AspNetCore.Components.Authorization;
using MudBlazor;
namespace MesaFabApproval.Client.Services;
public class MesaFabApprovalAuthStateProvider : AuthenticationStateProvider, IDisposable {
private readonly IAuthenticationService _authService;
private readonly IUserService _userService;
private readonly ISnackbar _snackbar;
public User? CurrentUser { get; private set; }
public MesaFabApprovalAuthStateProvider(IAuthenticationService authService,
ISnackbar snackbar,
IUserService userService) {
_authService = authService ??
throw new ArgumentNullException("IAuthenticationService not injected");
_snackbar = snackbar ??
throw new ArgumentNullException("ISnackbar not injected");
_userService = userService ??
throw new ArgumentNullException("IUserService not injected");
AuthenticationStateChanged += OnAuthenticationStateChangedAsync;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync() {
ClaimsPrincipal principal = new();
try {
principal = await _authService.FetchAuthState();
CurrentUser = await _authService.GetCurrentUser();
return new(principal);
} catch (Exception ex) {
return new(new ClaimsPrincipal());
}
}
public async Task StateHasChanged(ClaimsPrincipal principal) {
if (principal is null) throw new ArgumentNullException("ClaimsPrincipal cannot be null");
CurrentUser = await _authService.GetCurrentUser();
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
if (CurrentUser is null) {
string loginId = _userService.GetLoginIdFromClaimsPrincipal(principal);
User? user = await _userService.GetUserByLoginId(loginId);
await _authService.SetCurrentUser(user);
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
}
}
public async Task LoginAsync(string loginId, string password) {
try {
if (string.IsNullOrWhiteSpace(loginId)) throw new ArgumentException("LoginId cannot be null or empty");
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Password cannot be null or empty");
ClaimsPrincipal principal = await _authService.SendAuthenticationRequest(loginId, password);
CurrentUser = await _authService.GetCurrentUser();
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
} catch (Exception ex) {
_snackbar.Add(ex.Message, Severity.Error);
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(new())));
}
}
public async Task LoginLocal() {
try {
ClaimsPrincipal principal = await _authService.AttemptLocalUserAuth();
CurrentUser = await _authService.GetCurrentUser();
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
} catch (Exception ex) {
_snackbar.Add(ex.Message, Severity.Error);
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(new())));
}
}
public async Task Logout() {
CurrentUser = null;
await _authService.ClearTokens();
await _authService.ClearCurrentUser();
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(new())));
}
public void Dispose() => AuthenticationStateChanged -= OnAuthenticationStateChangedAsync;
private async void OnAuthenticationStateChangedAsync(Task<AuthenticationState> task) {
try {
AuthenticationState authenticationState = await task;
if (authenticationState is not null) {
ClaimsPrincipal principal = await _authService.FetchAuthState();
CurrentUser = await _authService.GetCurrentUser();
}
} catch (Exception ex) {
// _snackbar.Add($"Unable to fetch authentication state, because {ex.Message}", Severity.Error);
}
}
}

View File

@ -0,0 +1,767 @@
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using MesaFabApproval.Shared.Models;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Caching.Memory;
using MudBlazor;
namespace MesaFabApproval.Client.Services;
public interface IPCRBService {
Task<string> IdIsValid(string id);
Task<bool> IdIsValid(int id);
Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache);
Task CreateNewPCRB(PCRB pcrb);
Task<PCRB> GetPCRBByPlanNumber(int planNumber, bool bypassCache);
Task<PCRB> GetPCRBByTitle(string title, bool bypassCache);
Task UpdatePCRB(PCRB pcrb);
Task DeletePCRB(int planNumber);
Task UploadAttachment(PCRBAttachment attachment);
Task<IEnumerable<PCRBAttachment>> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache);
Task UpdateAttachment(PCRBAttachment attachment);
Task DeleteAttachment(PCRBAttachment attachment);
Task CreateNewActionItem(PCRBActionItem actionItem);
Task UpdateActionItem(PCRBActionItem actionItem);
Task DeleteActionItem(int id);
Task<IEnumerable<PCRBActionItem>> GetActionItemsForPlanNumber(int planNumber, bool bypassCache);
Task CreateNewAttendee(PCRBAttendee attendee);
Task UpdateAttendee(PCRBAttendee attendee);
Task DeleteAttendee(int id);
Task<IEnumerable<PCRBAttendee>> GetAttendeesByPlanNumber(int planNumber, bool bypassCache);
Task CreatePCR3Document(PCR3Document document);
Task UpdatePCR3Document(PCR3Document document);
Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache);
Task NotifyNewApprovals(PCRB pcrb);
Task NotifyApprovers(PCRBNotification notification);
Task NotifyOriginator(PCRBNotification notification);
Task NotifyResponsiblePerson(PCRBActionItemNotification notification);
}
public class PCRBService : IPCRBService {
private readonly IMemoryCache _cache;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ISnackbar _snackbar;
private readonly IUserService _userService;
public PCRBService(IMemoryCache cache,
IHttpClientFactory httpClientFactory,
ISnackbar snackbar,
IUserService userService) {
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException("IHttpClientFactory not injected");
_snackbar = snackbar ?? throw new ArgumentNullException("ISnackbar not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
}
public async Task<string> IdIsValid(string id) {
bool isMatch = true;
if (string.IsNullOrWhiteSpace(id)) isMatch = false;
try {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/getByPlanNumber?planNumber={id}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
PCRB? pcrb = JsonSerializer.Deserialize<PCRB>(responseContent, jsonSerializerOptions);
if (pcrb is null) isMatch = false;
} else {
isMatch = false;
}
} catch (Exception) {
isMatch = false;
}
if (!isMatch) return $"{id} is not a valid PCRB#";
return null;
}
public async Task<bool> IdIsValid(int id) {
bool isMatch = true;
if (id <= 0) isMatch = false;
try {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/getByPlanNumber?planNumber={id}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
PCRB? pcrb = JsonSerializer.Deserialize<PCRB>(responseContent, jsonSerializerOptions);
if (pcrb is null) isMatch = false;
} else {
isMatch = false;
}
} catch (Exception) {
isMatch = false;
}
return isMatch;
}
public async Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache) {
try {
IEnumerable<PCRB>? allPCRBs = null;
if (!bypassCache) allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs");
if (allPCRBs is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/all?bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
allPCRBs = JsonSerializer.Deserialize<IEnumerable<PCRB>>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse PCRBs from API response");
_cache.Set($"allPCRBs", allPCRBs, DateTimeOffset.Now.AddMinutes(15));
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
}
return allPCRBs;
} catch (Exception) {
throw;
}
}
public async Task CreateNewPCRB(PCRB pcrb) {
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(pcrb),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
PCRB newPCRB = await GetPCRBByTitle(pcrb.Title, true);
_cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1));
IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs");
if (allPCRBs is not null) {
List<PCRB> pcrbList = allPCRBs.ToList();
pcrbList.Add(newPCRB);
_cache.Set("allPCRBs", pcrbList);
}
}
public async Task<PCRB> GetPCRBByPlanNumber(int planNumber, bool bypassCache) {
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan #");
PCRB? pcrb = null;
if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{planNumber}");
if (pcrb is null && !bypassCache)
pcrb = _cache.Get<IEnumerable<PCRB>>("allPCRBs")?.FirstOrDefault(m => m.PlanNumber == planNumber);
if (pcrb is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage =
new(HttpMethod.Get, $"pcrb/getByPlanNumber?planNumber={planNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
pcrb = JsonSerializer.Deserialize<PCRB>(responseContent, jsonSerializerOptions) ??
throw new Exception("unable to parse PCRB from API response");
_cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1));
if (bypassCache) {
IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs");
if (allPCRBs is not null) {
List<PCRB> pcrbList = allPCRBs.ToList();
pcrbList.RemoveAll(p => p.PlanNumber == planNumber);
pcrbList.Add(pcrb);
_cache.Set("allPCRBs", pcrbList);
}
}
}
return pcrb;
}
public async Task<PCRB> GetPCRBByTitle(string title, bool bypassCache) {
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentNullException("title cannot be null");
PCRB? pcrb = null;
if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{title}");
if (pcrb is null && !bypassCache)
pcrb = _cache.Get<IEnumerable<PCRB>>("allPCRBs")?.FirstOrDefault(m => m.Title.Equals(title));
if (pcrb is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage =
new(HttpMethod.Get, $"pcrb/getByTitle?title={title}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
pcrb = JsonSerializer.Deserialize<PCRB>(responseContent, jsonSerializerOptions) ??
throw new Exception("unable to parse PCRB from API response");
_cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1));
if (bypassCache) {
IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs");
if (allPCRBs is not null) {
List<PCRB> pcrbList = allPCRBs.ToList();
pcrbList.RemoveAll(p => p.PlanNumber == pcrb.PlanNumber);
pcrbList.Add(pcrb);
_cache.Set("allPCRBs", pcrbList);
}
}
}
return pcrb;
}
public async Task UpdatePCRB(PCRB pcrb) {
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(pcrb),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
_cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1));
_cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1));
IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs");
if (allPCRBs is not null) {
List<PCRB> pcrbList = allPCRBs.ToList();
pcrbList.RemoveAll(m => m.PlanNumber == pcrb.PlanNumber);
pcrbList.Add(pcrb);
_cache.Set("allPCRBs", pcrbList);
}
}
public async Task DeletePCRB(int planNumber) {
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan #");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb?planNumber={planNumber}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) throw new Exception(responseMessage.ReasonPhrase);
IEnumerable<PCRB> allPCRBs = await GetAllPCRBs(true);
_cache.Set("allPCRBs", allPCRBs);
}
public async Task UploadAttachment(PCRBAttachment attachment) {
if (attachment is null) throw new ArgumentNullException("attachment cannot be null");
if (attachment.File is null) throw new ArgumentNullException("file cannot be null");
if (attachment.File.Size <= 0) throw new ArgumentException("file size must be greater than zero");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/attachment");
using MultipartFormDataContent content = new MultipartFormDataContent();
try {
long maxFileSize = 1024L * 1024L * 1024L * 2L;
StreamContent fileContent = new StreamContent(attachment.File.OpenReadStream(maxFileSize));
FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();
const string defaultContentType = "application/octet-stream";
if (!contentTypeProvider.TryGetContentType(attachment.File.Name, out string? contentType)) {
contentType = defaultContentType;
}
fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
content.Add(content: fileContent, name: "\"file\"", fileName: attachment.File.Name);
} catch (Exception ex) {
_snackbar.Add(ex.Message);
}
content.Add(new StringContent(attachment.PlanNumber.ToString()), "PlanNumber");
content.Add(new StringContent(attachment.FileName), "FileName");
content.Add(new StringContent(attachment.UploadedByID.ToString()), "UploadedByID");
content.Add(new StringContent(attachment.Title), "Title");
content.Add(new StringContent(attachment.UploadDateTime.ToString("yyyy-MM-dd HH:mm:ss")), "UploadDateTime");
content.Add(new StringContent(attachment.Step.ToString()), "Step");
requestMessage.Content = content;
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
await GetAttachmentsByPlanNumber(attachment.PlanNumber, true);
}
public async Task<IEnumerable<PCRBAttachment>> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache) {
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
IEnumerable<PCRBAttachment>? attachments = null;
if (!bypassCache)
attachments = _cache.Get<IEnumerable<PCRBAttachment>>($"pcrbAttachments{planNumber}");
if (attachments is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/attachments?planNumber={planNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
attachments = JsonSerializer.Deserialize<IEnumerable<PCRBAttachment>>(responseContent, jsonSerializerOptions) ??
new List<PCRBAttachment>();
if (attachments.Count() > 0) {
foreach (PCRBAttachment attachment in attachments) {
attachment.UploadedBy = await _userService.GetUserByUserId(attachment.UploadedByID);
}
_cache.Set($"pcrbAttachments{planNumber}", attachments, DateTimeOffset.Now.AddMinutes(5));
}
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
}
return attachments;
}
public async Task UpdateAttachment(PCRBAttachment attachment) {
if (attachment is null) throw new ArgumentNullException("attachment cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/attachment");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(attachment),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
}
public async Task DeleteAttachment(PCRBAttachment attachment) {
if (attachment is null) throw new ArgumentNullException("attachment cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb/attachment");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(attachment),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
}
public async Task CreateNewActionItem(PCRBActionItem actionItem) {
if (actionItem is null) throw new ArgumentNullException("action item cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/actionItem");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(actionItem),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
await GetActionItemsForPlanNumber(actionItem.PlanNumber, true);
}
public async Task UpdateActionItem(PCRBActionItem actionItem) {
if (actionItem is null) throw new ArgumentNullException("action item cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/actionItem");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(actionItem),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
}
public async Task DeleteActionItem(int id) {
if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB action item ID");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb/actionItem?id={id}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) throw new Exception(responseMessage.ReasonPhrase);
}
public async Task<IEnumerable<PCRBActionItem>> GetActionItemsForPlanNumber(int planNumber, bool bypassCache) {
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
IEnumerable<PCRBActionItem>? actionItems = null;
if (!bypassCache)
actionItems = _cache.Get<IEnumerable<PCRBActionItem>>($"pcrbActionItems{planNumber}");
if (actionItems is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/actionItems?planNumber={planNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
actionItems = JsonSerializer.Deserialize<IEnumerable<PCRBActionItem>>(responseContent, jsonSerializerOptions) ??
new List<PCRBActionItem>();
if (actionItems.Count() > 0) {
foreach (PCRBActionItem actionItem in actionItems) {
actionItem.UploadedBy = await _userService.GetUserByUserId(actionItem.UploadedByID);
if (actionItem.ResponsiblePersonID > 0)
actionItem.ResponsiblePerson = await _userService.GetUserByUserId(actionItem.ResponsiblePersonID);
if (actionItem.ClosedByID > 0)
actionItem.ClosedBy = await _userService.GetUserByUserId(actionItem.ClosedByID);
}
_cache.Set($"pcrbActionItems{planNumber}", actionItems, DateTimeOffset.Now.AddMinutes(5));
}
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
}
return actionItems;
}
public async Task CreateNewAttendee(PCRBAttendee attendee) {
if (attendee is null) throw new ArgumentNullException("attendee cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/attendee");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(attendee),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
await GetAttendeesByPlanNumber(attendee.PlanNumber, true);
}
public async Task UpdateAttendee(PCRBAttendee attendee) {
if (attendee is null) throw new ArgumentNullException("attendee cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/attendee");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(attendee),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
}
public async Task DeleteAttendee(int id) {
if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB attendee ID");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb/attendee?id={id}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) throw new Exception(responseMessage.ReasonPhrase);
}
public async Task<IEnumerable<PCRBAttendee>> GetAttendeesByPlanNumber(int planNumber, bool bypassCache) {
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
IEnumerable<PCRBAttendee>? attendees = null;
if (!bypassCache)
attendees = _cache.Get<IEnumerable<PCRBAttendee>>($"pcrbAttendees{planNumber}");
if (attendees is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/attendees?planNumber={planNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
attendees = JsonSerializer.Deserialize<IEnumerable<PCRBAttendee>>(responseContent, jsonSerializerOptions) ??
new List<PCRBAttendee>();
if (attendees.Count() > 0) {
foreach (PCRBAttendee attendee in attendees)
attendee.Attendee = await _userService.GetUserByUserId(attendee.AttendeeID);
_cache.Set($"pcrbAttendees{planNumber}", attendees, DateTimeOffset.Now.AddMinutes(5));
}
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
}
return attendees;
}
public async Task CreatePCR3Document(PCR3Document document) {
if (document is null) throw new ArgumentNullException("document cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/pcr3Document");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(document),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
await GetPCR3DocumentsForPlanNumber(document.PlanNumber, true);
}
public async Task UpdatePCR3Document(PCR3Document document) {
if (document is null) throw new ArgumentNullException("document cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/pcr3Document");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(document),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
}
public async Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache) {
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
IEnumerable<PCR3Document>? documents = null;
if (!bypassCache)
documents = _cache.Get<IEnumerable<PCR3Document>>($"pcr3Documents{planNumber}");
if (documents is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/pcr3Documents?planNumber={planNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
documents = JsonSerializer.Deserialize<IEnumerable<PCR3Document>>(responseContent, jsonSerializerOptions) ??
new List<PCR3Document>();
if (documents.Count() > 0) {
foreach (PCR3Document document in documents) {
if (document.CompletedByID > 0)
document.CompletedBy = await _userService.GetUserByUserId(document.CompletedByID);
}
_cache.Set($"pcr3Documents{planNumber}", documents, DateTimeOffset.Now.AddMinutes(5));
}
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
}
return documents;
}
public async Task NotifyNewApprovals(PCRB pcrb) {
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/notify/new-approvals");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(pcrb),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to notify new PCRB approvers, because {responseMessage.ReasonPhrase}");
}
public async Task NotifyApprovers(PCRBNotification notification) {
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/notify/approvers");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(notification),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to notify PCRB approvers, because {responseMessage.ReasonPhrase}");
}
public async Task NotifyOriginator(PCRBNotification notification) {
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/notify/originator");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(notification),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to notify PCRB originator, because {responseMessage.ReasonPhrase}");
}
public async Task NotifyResponsiblePerson(PCRBActionItemNotification notification) {
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/notify/responsiblePerson");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(notification),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to notify PCRB responsible person, because {responseMessage.ReasonPhrase}");
}
}

View File

@ -0,0 +1,208 @@
using System.Security.Claims;
using System.Text.Json;
using MesaFabApproval.Shared.Models;
using Microsoft.AspNetCore.Components.WebAssembly.Http;
using Microsoft.Extensions.Caching.Memory;
namespace MesaFabApproval.Client.Services;
public interface IUserService {
ClaimsPrincipal GetClaimsPrincipalFromUser(User user);
string GetLoginIdFromClaimsPrincipal(ClaimsPrincipal claimsPrincipal);
Task<User> GetUserFromClaimsPrincipal(ClaimsPrincipal claimsPrincipal);
Task<User> GetUserByUserId(int userId);
Task<User> GetUserByLoginId(string loginId);
Task<IEnumerable<User>> GetAllActiveUsers();
Task<IEnumerable<int>> GetApproverUserIdsBySubRoleCategoryItem(string item);
}
public class UserService : IUserService {
private readonly IMemoryCache _cache;
private readonly IHttpClientFactory _httpClientFactory;
public UserService(IMemoryCache cache, IHttpClientFactory httpClientFactory) {
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException("IHttpClientFactory not injected");
}
public ClaimsPrincipal GetClaimsPrincipalFromUser(User user) {
if (user is null) throw new ArgumentNullException("user cannot be null");
List<Claim> claims = new() {
new Claim(nameof(user.LoginID), user.LoginID)
};
if (user.IsManager) claims.Add(new Claim(ClaimTypes.Role, "manager"));
if (user.IsAdmin) claims.Add(new Claim(ClaimTypes.Role, "admin"));
ClaimsIdentity identity = new ClaimsIdentity(claims, "MesaFabApprovalWasm");
return new ClaimsPrincipal(identity);
}
public async Task<User> GetUserByUserId(int userId) {
if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID");
User? user = _cache.Get<User>($"user{userId}");
if (user is null)
user = _cache.Get<IEnumerable<User>>("allActiveUsers")?.FirstOrDefault(u => u.UserID == userId);
if (user is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"user/userId?userId={userId}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
user = JsonSerializer.Deserialize<User>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse user from API response");
_cache.Set($"user{userId}", user, DateTimeOffset.Now.AddDays(1));
} else {
throw new Exception($"GetUserByUserId failed for user {userId}, because {responseMessage.ReasonPhrase}");
}
}
if (user is null) throw new Exception($"User for userId {userId} not found");
return user;
}
public async Task<User> GetUserByLoginId(string loginId) {
if (string.IsNullOrWhiteSpace(loginId))
throw new ArgumentNullException("loginId cannot be null or empty");
User? user = _cache.Get<User>($"user{loginId}");
if (user is null)
user = _cache.Get<IEnumerable<User>>("allActiveUsers")?.FirstOrDefault(u => u.LoginID == loginId);
if (user is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"user/loginId?loginId={loginId}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
user = JsonSerializer.Deserialize<User>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse user from API response");
_cache.Set($"user{loginId}", user, DateTimeOffset.Now.AddDays(1));
} else {
throw new Exception($"GetUserByLoginId failed for {loginId}, because {responseMessage.ReasonPhrase}");
}
}
if (user is null) throw new Exception($"User for loginId {loginId} not found");
return user;
}
public async Task<IEnumerable<User>> GetAllActiveUsers() {
IEnumerable<User>? activeUsers = _cache.Get<IEnumerable<User>>("allActiveUsers");
if (activeUsers is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"users/active");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
activeUsers = JsonSerializer.Deserialize<IEnumerable<User>>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse user from API response");
_cache.Set("allActiveUsers", activeUsers, DateTimeOffset.Now.AddHours(1));
} else {
throw new Exception($"Cannot get all active users, because {responseMessage.ReasonPhrase}");
}
}
if (activeUsers is null)
throw new Exception("unable to fetch all active users");
return activeUsers;
}
public string GetLoginIdFromClaimsPrincipal(ClaimsPrincipal principal) {
if (principal is null) throw new ArgumentNullException("Principal cannot be null");
Claim loginIdClaim = principal.FindFirst("LoginID") ??
throw new Exception("LoginID claim not found in principal");
string loginId = loginIdClaim.Value;
return loginId;
}
public async Task<User> GetUserFromClaimsPrincipal(ClaimsPrincipal claimsPrincipal) {
if (claimsPrincipal is null) throw new ArgumentNullException("ClaimsPrincipal cannot be null");
Claim loginIdClaim = claimsPrincipal.FindFirst("LoginID") ??
throw new Exception("LoginID claim not found in principal");
string loginId = loginIdClaim.Value ??
throw new Exception("LoginID claim value is null");
User user = await GetUserByLoginId(loginId) ??
throw new Exception($"User for loginId {loginId} not found");
return user;
}
public async Task<IEnumerable<int>> GetApproverUserIdsBySubRoleCategoryItem(string item) {
if (string.IsNullOrWhiteSpace(item)) throw new ArgumentException("SubRoleCategoryItem cannot be null or empty");
IEnumerable<int>? approverUserIds = _cache.Get<IEnumerable<int>>($"approvers{item}");
if (approverUserIds is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"approver?subRoleCategoryItem={item}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
approverUserIds = JsonSerializer.Deserialize<IEnumerable<int>>(responseContent, jsonSerializerOptions) ??
throw new Exception("Unable to parse user from API response");
_cache.Set($"approvers{item}", approverUserIds, DateTimeOffset.Now.AddDays(1));
} else {
throw new Exception($"Unable to get approvers for SubRoleCategoryItem {item}, because {responseMessage.ReasonPhrase}");
}
}
if (approverUserIds is null) throw new Exception($"Approvers for SubRoleCategoryItem {item} not found");
return approverUserIds;
}
}

Some files were not shown because too many files have changed in this diff Show More