Compare commits
	
		
			1 Commits
		
	
	
		
			ae8710e6d8
			...
			mrb
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2dbb541c1a | 
							
								
								
									
										281
									
								
								.editorconfig
									
									
									
									
									
								
							
							
						
						
									
										281
									
								
								.editorconfig
									
									
									
									
									
								
							| @ -1,74 +1,229 @@ | ||||
| # EditorConfig is awesome:http://EditorConfig.org | ||||
|  | ||||
| # top-most EditorConfig file | ||||
| # Remove the line below if you want to inherit .editorconfig settings from higher directories | ||||
| 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: | ||||
| # C# files | ||||
| [*.cs] | ||||
| # Sort using and Import directives with System.* appearing first | ||||
|  | ||||
| #### 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 | ||||
|  | ||||
| # Don't use this. qualifier | ||||
| dotnet_style_qualification_for_field = false:suggestion | ||||
| dotnet_style_qualification_for_property = false:suggestion | ||||
| # 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 | ||||
|  | ||||
| # use int x = .. over Int32 | ||||
| dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion | ||||
| # Language keywords vs BCL types preferences | ||||
| dotnet_style_predefined_type_for_locals_parameters_members = true | ||||
| dotnet_style_predefined_type_for_member_access = true | ||||
|  | ||||
| # use int.MaxValue over Int32.MaxValue | ||||
| dotnet_style_predefined_type_for_member_access = true:suggestion | ||||
| # 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 | ||||
|  | ||||
| # 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 | ||||
| # Modifier preferences | ||||
| dotnet_style_require_accessibility_modifiers = for_non_interface_members | ||||
|  | ||||
| # Disallow throw expressions. | ||||
| csharp_style_throw_expression = false:suggestion | ||||
| # 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 | ||||
|  | ||||
| # 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 | ||||
| # 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 | ||||
|  | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -337,3 +337,6 @@ ASALocalRun/ | ||||
| !.vscode/tasks.json | ||||
| !.vscode/launch.json | ||||
| !.vscode/extensions.json | ||||
| .vscode | ||||
|  | ||||
| .env | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
| @ -12,20 +12,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.Net.Http.Headers; | ||||
| using System.Text; | ||||
|  | ||||
| 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; | ||||
|         } | ||||
|  | ||||
| @ -36,8 +40,7 @@ namespace Fab2ApprovalSystem.Controllers | ||||
|         [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) | ||||
|         { | ||||
|         public ActionResult Login(string returnUrl) { | ||||
|             ViewBag.ReturnUrl = returnUrl; | ||||
|             return View(); | ||||
|         } | ||||
| @ -45,18 +48,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 +82,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 +110,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 +129,87 @@ 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."); | ||||
|         //        } | ||||
|         //    } | ||||
|  | ||||
|         //    // If we got this far, something failed, redisplay form | ||||
|         //    return View(model); | ||||
|         //} | ||||
|  | ||||
|         // | ||||
|         // 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 +220,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 +275,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); | ||||
|  | ||||
| @ -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,36 +325,29 @@ 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 | ||||
| @ -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; | ||||
| @ -506,90 +386,75 @@ namespace Fab2ApprovalSystem.Controllers | ||||
|  | ||||
|             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; | ||||
|         } | ||||
|     } | ||||
|      | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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"> | ||||
|  | ||||
| @ -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,8 +320,7 @@ 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 | ||||
| @ -398,31 +331,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") { | ||||
|                     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; | ||||
|  | ||||
| @ -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,10 +173,8 @@ 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"; | ||||
|  | ||||
| @ -243,17 +203,13 @@ namespace Fab2ApprovalSystem.Misc | ||||
|                     return "D8"; | ||||
|                 case GlobalVars.CASection.CF: // CA Findings  | ||||
|                     return "CF"; | ||||
|                      | ||||
|  | ||||
|             } | ||||
|  | ||||
|             return ""; | ||||
|         } | ||||
|  | ||||
|         public static string DocumentTypeMapper(GlobalVars.DocumentType docType) | ||||
|         { | ||||
|             switch (docType) | ||||
|             { | ||||
|         public static string DocumentTypeMapper(GlobalVars.DocumentType docType) { | ||||
|             switch (docType) { | ||||
|                 case GlobalVars.DocumentType.Audit: | ||||
|                     return "Audit"; | ||||
|                 case GlobalVars.DocumentType.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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
							
								
								
									
										7
									
								
								Fab2ApprovalSystem/Models/AuthAttempt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Fab2ApprovalSystem/Models/AuthAttempt.cs
									
									
									
									
									
										Normal 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; } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										6
									
								
								Fab2ApprovalSystem/Models/AuthTokens.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Fab2ApprovalSystem/Models/AuthTokens.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| namespace Fab2ApprovalSystem.Models { | ||||
|     public class AuthTokens { | ||||
|         public string JwtToken { get; set; } | ||||
|         public string RefreshToken { get; set; } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										6
									
								
								Fab2ApprovalSystem/Models/LoginResult.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Fab2ApprovalSystem/Models/LoginResult.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| namespace Fab2ApprovalSystem.Models { | ||||
|     public class LoginResult { | ||||
|         public bool IsAuthenticated { get; set; } | ||||
|         public AuthTokens AuthTokens { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -125,6 +125,14 @@ | ||||
|             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); | ||||
|             //menu.Add().Text("Special Work Requests").Action("SpecialWorkRequestList", "Home"); | ||||
|             //menu.Add().Text("PCRB").Action("ChangeControlList", "Home"); | ||||
|             //menu.Add().Text("MRB").Action("MRBList", "Home"); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
							
								
								
									
										13
									
								
								MesaFabApproval.API/.config/dotnet-tools.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								MesaFabApproval.API/.config/dotnet-tools.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| { | ||||
|   "version": 1, | ||||
|   "isRoot": true, | ||||
|   "tools": { | ||||
|     "dotnet-ef": { | ||||
|       "version": "8.0.6", | ||||
|       "commands": [ | ||||
|         "dotnet-ef" | ||||
|       ], | ||||
|       "rollForward": false | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										20
									
								
								MesaFabApproval.API/Clients/SmtpClientWrapper.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								MesaFabApproval.API/Clients/SmtpClientWrapper.cs
									
									
									
									
									
										Normal 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 SmtpClient _client; | ||||
|  | ||||
|     public SmtpClientWrapper(SmtpClient client) { | ||||
|         _client = client ?? | ||||
|             throw new ArgumentNullException("SmtpClient not injected"); | ||||
|     } | ||||
|  | ||||
|     public void Send(MailMessage message) { | ||||
|         _client.Send(message); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										401
									
								
								MesaFabApproval.API/Controllers/ApprovalController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										401
									
								
								MesaFabApproval.API/Controllers/ApprovalController.cs
									
									
									
									
									
										Normal 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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										120
									
								
								MesaFabApproval.API/Controllers/AuthenticationController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								MesaFabApproval.API/Controllers/AuthenticationController.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | ||||
| using System.Security.Authentication; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
| using MesaFabApproval.Shared.Services; | ||||
|  | ||||
| using MesaFabApprovalAPI.Services; | ||||
|  | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
|  | ||||
| namespace MesaFabApproval.API.Controllers; | ||||
|  | ||||
| [ApiController] | ||||
| 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] | ||||
|     [AllowAnonymous] | ||||
|     [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] | ||||
|     [AllowAnonymous] | ||||
|     [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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										705
									
								
								MesaFabApproval.API/Controllers/MRBController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										705
									
								
								MesaFabApproval.API/Controllers/MRBController.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,705 @@ | ||||
| using MesaFabApproval.API.Services; | ||||
| 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; | ||||
|  | ||||
|     public MRBController(ILogger<MRBController> logger, IMRBService mrbService, IMonInWorkerClient monInClient) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _mrbService = mrbService ?? throw new ArgumentNullException("IMRBService not injected"); | ||||
|         _monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected"); | ||||
|     } | ||||
|  | ||||
|     [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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("mrb/all")] | ||||
|     public async Task<IActionResult> GetAllMRBs() { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
|  | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get all MRBs"); | ||||
|  | ||||
|             IEnumerable<MRB> allMrbs = await _mrbService.GetAllMRBs(); | ||||
|  | ||||
|             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/getById")] | ||||
|     public async Task<IActionResult> GetMRBById(int id) { | ||||
|         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); | ||||
|  | ||||
|             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 emtpy"); | ||||
|  | ||||
|             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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										223
									
								
								MesaFabApproval.API/Controllers/UserController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								MesaFabApproval.API/Controllers/UserController.cs
									
									
									
									
									
										Normal 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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										32
									
								
								MesaFabApproval.API/MesaFabApproval.API.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								MesaFabApproval.API/MesaFabApproval.API.csproj
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.Web"> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <TargetFramework>net8.0</TargetFramework> | ||||
|     <Nullable>enable</Nullable> | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Dapper" Version="2.1.35" /> | ||||
|     <PackageReference Include="Dapper.Contrib" Version="2.0.78" /> | ||||
|     <PackageReference Include="dotenv.net" Version="3.1.3" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.5" /> | ||||
|     <PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.0" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" /> | ||||
|     <PackageReference Include="NLog" Version="5.3.2" /> | ||||
|     <PackageReference Include="NLog.Web.AspNetCore" Version="5.3.11" /> | ||||
|     <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.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> | ||||
							
								
								
									
										127
									
								
								MesaFabApproval.API/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								MesaFabApproval.API/Program.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | ||||
| using MesaFabApproval.Shared.Services; | ||||
| using NLog.Web; | ||||
| using MesaFabApprovalAPI.Services; | ||||
| using Microsoft.OpenApi.Models; | ||||
| using dotenv.net; | ||||
| using Microsoft.AspNetCore.Authentication.JwtBearer; | ||||
| using Microsoft.IdentityModel.Tokens; | ||||
| using System.Text; | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using MesaFabApproval.API.Services; | ||||
| using NLog.Extensions.Logging; | ||||
| using MesaFabApproval.API.Clients; | ||||
| using System.Net.Mail; | ||||
|  | ||||
| WebApplicationBuilder builder = WebApplication.CreateBuilder(args); | ||||
|  | ||||
| DotEnv.Load(); | ||||
|  | ||||
| builder.Logging.ClearProviders(); | ||||
| builder.Logging.SetMinimumLevel(LogLevel.Trace); | ||||
| builder.Logging.AddNLog(); | ||||
|  | ||||
| builder.Services.AddMemoryCache(); | ||||
|  | ||||
| string jwtIssuer = Environment.GetEnvironmentVariable("FabApprovalJwtIssuer") ?? | ||||
|     throw new ArgumentNullException("FabApprovalJwtIssuer environment variable not found"); | ||||
| string jwtAudience = Environment.GetEnvironmentVariable("FabApprovalJwtAudience") ?? | ||||
|     throw new ArgumentNullException("FabApprovalJwtAudience environment variable not found"); | ||||
| string jwtKey = Environment.GetEnvironmentVariable("FabApprovalJwtKey") ?? | ||||
|     throw new ArgumentNullException("FabApprovalJwtKey environment variable not found"); | ||||
|  | ||||
| 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 = jwtIssuer, | ||||
|         ValidateAudience = false, | ||||
|         IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)), | ||||
|         ClockSkew = TimeSpan.Zero | ||||
|     }; | ||||
| }); | ||||
|  | ||||
| builder.Services.AddAuthorization(options => { | ||||
|     options.DefaultPolicy = new AuthorizationPolicyBuilder() | ||||
|                                 .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme) | ||||
|                                 .RequireAuthenticatedUser() | ||||
|                                 .Build(); | ||||
| }); | ||||
|  | ||||
| builder.Services.AddHttpClient(); | ||||
|  | ||||
| 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<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<IApprovalService, ApprovalService>(); | ||||
|  | ||||
| builder.Services.AddControllers(); | ||||
|  | ||||
| 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[] { } | ||||
|     } | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| builder.Services.AddCors(options => { | ||||
|     options.AddDefaultPolicy( | ||||
|         builder => { | ||||
|             builder.AllowAnyOrigin() | ||||
|                    .AllowAnyHeader() | ||||
|                    .AllowAnyMethod(); | ||||
|         }); | ||||
| }); | ||||
|  | ||||
| WebApplication app = builder.Build(); | ||||
|  | ||||
| app.UseCors(); | ||||
|  | ||||
| // Configure the HTTP request pipeline. | ||||
| if (app.Environment.IsDevelopment()) | ||||
| { | ||||
|     app.UseSwagger(); | ||||
|     app.UseSwaggerUI(); | ||||
| } | ||||
|  | ||||
| app.UseHttpsRedirection(); | ||||
|  | ||||
| app.UseAuthentication(); | ||||
|  | ||||
| app.UseAuthorization(); | ||||
|  | ||||
| app.MapControllers(); | ||||
|  | ||||
| app.Run(); | ||||
| @ -0,0 +1,22 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- | ||||
| https://go.microsoft.com/fwlink/?LinkID=208121. | ||||
| --> | ||||
| <Project> | ||||
|   <PropertyGroup> | ||||
|     <DeleteExistingFiles>false</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> | ||||
|   </PropertyGroup> | ||||
| </Project> | ||||
							
								
								
									
										320
									
								
								MesaFabApproval.API/Services/ApprovalService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										320
									
								
								MesaFabApproval.API/Services/ApprovalService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,320 @@ | ||||
| using System.Net.Mail; | ||||
| using System.Text; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
|  | ||||
| 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, '{DateTime.Now.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}'"); | ||||
|  | ||||
|                 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.AddHours(1)); | ||||
|                 } | ||||
|  | ||||
|                 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.AddHours(1)); | ||||
|             } | ||||
|  | ||||
|             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}, "); | ||||
|             if (approval.NotifyDate > DateTime.MinValue) | ||||
|                 queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"AssignedDate='{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (approval.CompletedDate < DateTime.MaxValue) | ||||
|                 queryBuilder.Append($"CompletedDate='{approval.CompletedDate.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 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 > DateTime.MinValue) | ||||
|                 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 > DateTime.MinValue) | ||||
|                 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										213
									
								
								MesaFabApproval.API/Services/AuthenticationService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								MesaFabApproval.API/Services/AuthenticationService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,213 @@ | ||||
| using System.DirectoryServices.AccountManagement; | ||||
| using System.IdentityModel.Tokens.Jwt; | ||||
| using System.Security.Authentication; | ||||
| using System.Security.Claims; | ||||
| using System.Security.Cryptography; | ||||
| using System.Text; | ||||
|  | ||||
| using MesaFabApproval.API.Services; | ||||
| 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 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) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _userService = userService ?? throw new ArgumentNullException("IUserService not injected"); | ||||
|  | ||||
|         _jwtIssuer = Environment.GetEnvironmentVariable("FabApprovalJwtIssuer") ?? | ||||
|             throw new ArgumentNullException("FabApprovalJwtIssuer environment variable not found"); | ||||
|         _jwtAudience = Environment.GetEnvironmentVariable("FabApprovalJwtAudience") ?? | ||||
|             throw new ArgumentNullException("FabApprovalJwtAudience environment variable not found"); | ||||
|         _jwtKey = Environment.GetEnvironmentVariable("FabApprovalJwtKey") ?? | ||||
|             throw new ArgumentNullException("FabApprovalJwtKey environment variable not found"); | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|  | ||||
|                     _cache.Set<string>(login.LoginID, tokens.RefreshToken); | ||||
|  | ||||
|                     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 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.AddMinutes(5), | ||||
|                 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(); | ||||
|  | ||||
|             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); | ||||
|  | ||||
|                 _cache.Set<string>(authAttempt.LoginID, refreshedTokens.RefreshToken, DateTimeOffset.Now.AddHours(1)); | ||||
|  | ||||
|                 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"); | ||||
|  | ||||
|             string? cachedRefreshToken = _cache.Get<string>(loginId); | ||||
|  | ||||
|             if (cachedRefreshToken is null) return false; | ||||
|  | ||||
|             if (cachedRefreshToken.Equals(refreshToken)) return true; | ||||
|  | ||||
|             return false; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to validate refresh token. Exception: {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										89
									
								
								MesaFabApproval.API/Services/DalService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								MesaFabApproval.API/Services/DalService.cs
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										22
									
								
								MesaFabApproval.API/Services/DbConnectionService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								MesaFabApproval.API/Services/DbConnectionService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| using System.Data; | ||||
|  | ||||
| using Microsoft.Data.SqlClient; | ||||
|  | ||||
| namespace MesaFabApproval.API.Services; | ||||
|  | ||||
| public interface IDbConnectionService { | ||||
|     IDbConnection GetConnection(); | ||||
| } | ||||
|  | ||||
| public class DbConnectionService : IDbConnectionService { | ||||
|     private readonly string _dbConnectionString; | ||||
|  | ||||
|     public DbConnectionService() { | ||||
|         _dbConnectionString = Environment.GetEnvironmentVariable("FabApprovalDbConnectionString") ?? | ||||
|             throw new ArgumentNullException("FabApprovalDbConnectionString environment variable not found"); | ||||
|     } | ||||
|  | ||||
|     public IDbConnection GetConnection() { | ||||
|         return new SqlConnection(_dbConnectionString); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										608
									
								
								MesaFabApproval.API/Services/MRBService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										608
									
								
								MesaFabApproval.API/Services/MRBService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,608 @@ | ||||
| using System.Net; | ||||
| using System.Net.Mail; | ||||
| using System.Text; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
|  | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
|  | ||||
| namespace MesaFabApproval.API.Services; | ||||
|  | ||||
| public interface IMRBService { | ||||
|     Task<IEnumerable<MRB>> GetAllMRBs(); | ||||
|     Task<MRB> GetMRBById(int id); | ||||
|     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<MRBAttachment>> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache); | ||||
|     Task DeleteAttachment(MRBAttachment attachment); | ||||
|     Task NotifyNewApprovals(MRB mrb); | ||||
|     Task NotifyApprovers(MRBNotification notification); | ||||
|     Task NotifyOriginator(MRBNotification notification); | ||||
| } | ||||
|  | ||||
| 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) { | ||||
|         _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 = Environment.GetEnvironmentVariable("NewFabApprovalBaseUrl") ?? | ||||
|             throw new ArgumentNullException("FabApprovalBaseUrl environment variable not found"); | ||||
|         _mrbAttachmentPath = Environment.GetEnvironmentVariable("FabApprovalMrbAttachmentPath") ?? | ||||
|             throw new ArgumentNullException("FabApprovalMrbAttachmentPath environment variable not found"); | ||||
|     } | ||||
|     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, "); | ||||
|             if (mrb.SubmittedDate > DateTime.MinValue) queryBuilder.Append("SubmittedDate, "); | ||||
|             if (mrb.CloseDate < DateTime.MaxValue) queryBuilder.Append("CloseDate, "); | ||||
|             if (mrb.CancelDate < DateTime.MaxValue) queryBuilder.Append("CancelDate, "); | ||||
|             queryBuilder.Append("NumberOfLotsAffected, "); | ||||
|             if (mrb.ApprovalDate < DateTime.MaxValue) queryBuilder.Append("ApprovalDate, "); | ||||
|             queryBuilder.Append("IssueDescription, CustomerImpacted, Department, Process, Val, RMANo, "); | ||||
|             queryBuilder.Append("PCRBNo, SpecsImpacted, TrainingRequired, Status, StageNo) values ("); | ||||
|             queryBuilder.Append($"{mrb.OriginatorID}, '{mrb.Title}', "); | ||||
|             if (mrb.SubmittedDate > DateTime.MinValue) | ||||
|                 queryBuilder.Append($"'{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CloseDate < DateTime.MaxValue) | ||||
|                 queryBuilder.Append($"'{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CancelDate < DateTime.MaxValue) | ||||
|                 queryBuilder.Append($"'{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"{mrb.NumberOfLotsAffected}, "); | ||||
|             if (mrb.ApprovalDate < DateTime.MaxValue) | ||||
|                 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});"); | ||||
|  | ||||
|             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() { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get all MRBs"); | ||||
|  | ||||
|             IEnumerable<MRB>? 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)); | ||||
|             } | ||||
|  | ||||
|             if (allMrbs is null || allMrbs.Count() == 0) | ||||
|                 throw new Exception("No MRBs found"); | ||||
|  | ||||
|             return allMrbs; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to get all MRBs. Exception: {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<MRB> GetMRBById(int id) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get an MRB by ID"); | ||||
|  | ||||
|             if (id < 0) throw new ArgumentException("Invalid MRB number"); | ||||
|  | ||||
|             MRB? 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 emtpy"); | ||||
|  | ||||
|             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}', "); | ||||
|             if (mrb.SubmittedDate > DateTime.MinValue) | ||||
|                 queryBuilder.Append($"SubmittedDate = '{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CloseDate < DateTime.MaxValue)  | ||||
|                 queryBuilder.Append($"CloseDate = '{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CancelDate < DateTime.MaxValue) | ||||
|                 queryBuilder.Append($"CancelDate = '{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"NumberOfLotsAffected = {mrb.NumberOfLotsAffected}, "); | ||||
|             if (mrb.ApprovalDate < DateTime.MaxValue)  | ||||
|                 queryBuilder.Append($"ApprovalDate = '{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"IssueDescription = '{mrb.IssueDescription}', "); | ||||
|             queryBuilder.Append($"CustomerImpacted = {Convert.ToInt32(mrb.CustomerImpacted)}, "); | ||||
|             queryBuilder.Append($"Department = '{mrb.Department}', "); | ||||
|             queryBuilder.Append($"Process = '{mrb.Process}', "); | ||||
|             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($"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($"values ('{mrbAction.Action}', '{mrbAction.Customer}', {mrbAction.Quantity}, "); | ||||
|             queryBuilder.Append($"'{mrbAction.PartNumber}', '{mrbAction.LotNumber}', {mrbAction.MRBNumber});"); | ||||
|  | ||||
|             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}', "); | ||||
|             queryBuilder.Append($"Quantity = {mrbAction.Quantity}, "); | ||||
|             queryBuilder.Append($"PartNumber = '{mrbAction.PartNumber}', "); | ||||
|             queryBuilder.Append($"LotNumber = '{mrbAction.LotNumber}', "); | ||||
|             if (mrbAction.AssignedDate > DateTime.MinValue) | ||||
|                 queryBuilder.Append($"AssignedDate= '{mrbAction.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrbAction.CompletedDate < DateTime.MaxValue) | ||||
|                 queryBuilder.Append($"CompletedDate= '{mrbAction.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"CompletedByUserID={mrbAction.CompletedByUserID} "); | ||||
|             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"); | ||||
|  | ||||
|             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}\\{mrbNumber}\\{encodedName}"; | ||||
|  | ||||
|                     taskList.Add(SaveFileToFileSystem(file, path)); | ||||
|                     taskList.Add(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); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Task.WaitAll(taskList.ToArray()); | ||||
|  | ||||
|             return uploadResults; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to upload 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 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 <= DateTime.MinValue).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}"; | ||||
|  | ||||
|                     StringBuilder bodyBuilder = new(); | ||||
|                     bodyBuilder.Append($"You have been assigned a new task in Mesa Fab Approval for issue {approval.IssueID}. The assigned role is {approval.SubRoleCategoryItem}. <br /> <br />"); | ||||
|                     bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?jwt=jwt&refreshToken=refreshToken&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); | ||||
|  | ||||
|             List<Approval> approvalsNeedingNotification = approvals.Where(a => a.NotifyDate <= DateTime.MinValue).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 = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber}"; | ||||
|  | ||||
|                     StringBuilder bodyBuilder = new(); | ||||
|                     bodyBuilder.Append($"{notification.Message} <br /> <br />"); | ||||
|                     bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?jwt=jwt&refreshToken=refreshToken&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}"; | ||||
|  | ||||
|             StringBuilder bodyBuilder = new(); | ||||
|             bodyBuilder.Append($"{notification.Message} <br /> <br />"); | ||||
|             bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?jwt=jwt&refreshToken=refreshToken&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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task SaveFileToFileSystem(IFormFile file, string path) { | ||||
|         try { | ||||
|             _logger.LogInformation($"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 (FileStream stream = File.Create(path)) { | ||||
|                 await file.OpenReadStream().CopyToAsync(stream); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to save file to file system. Exception: {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 file system. Exception: {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										79
									
								
								MesaFabApproval.API/Services/SmtpService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								MesaFabApproval.API/Services/SmtpService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | ||||
| using MesaFabApproval.API.Clients; | ||||
|  | ||||
| using Microsoft.IdentityModel.Tokens; | ||||
|  | ||||
| using System.Net.Mail; | ||||
|  | ||||
| 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) { | ||||
|         _logger = logger ?? | ||||
|             throw new ArgumentNullException("ILogger not injected"); | ||||
|         _smtpClient = smtpClient ?? | ||||
|             throw new ArgumentNullException("SmtpClient not injected"); | ||||
|         if (!Boolean.TryParse(Environment.GetEnvironmentVariable("FabApprovalShouldSendEmail"), out _shouldSendEmail)) | ||||
|             throw new ArgumentNullException("FabApprovalShouldSendEmail environment variable not found"); | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|         }); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										147
									
								
								MesaFabApproval.API/Services/UserService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								MesaFabApproval.API/Services/UserService.cs
									
									
									
									
									
										Normal 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										8
									
								
								MesaFabApproval.API/appsettings.Development.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								MesaFabApproval.API/appsettings.Development.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| { | ||||
|   "Logging": { | ||||
|     "LogLevel": { | ||||
|       "Default": "Information", | ||||
|       "Microsoft.AspNetCore": "Warning" | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										9
									
								
								MesaFabApproval.API/appsettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								MesaFabApproval.API/appsettings.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| { | ||||
|   "Logging": { | ||||
|     "LogLevel": { | ||||
|       "Default": "Information", | ||||
|       "Microsoft.AspNetCore": "Warning" | ||||
|     } | ||||
|   }, | ||||
|   "AllowedHosts": "*" | ||||
| } | ||||
							
								
								
									
										32
									
								
								MesaFabApproval.API/nLog.Development.config
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								MesaFabApproval.API/nLog.Development.config
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| <?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="asyncFile" xsi:type="AsyncWrapper"> | ||||
|             <target  | ||||
|                 name="appLog"  | ||||
|                 xsi:type="File"  | ||||
|                 fileName="d:\logs\MesaProveInApi\log.txt" | ||||
|                 archiveFilename="d:\logs\MesaProveInApi\archive\log-${shortdate}.txt" | ||||
|                 maxArchiveFiles="15" | ||||
|                 archiveEvery="Day" | ||||
|             /> | ||||
|             <target  | ||||
|                 name="consoleLog" | ||||
|                 xsi:type="Console" | ||||
|             /> | ||||
|         </target> | ||||
|     </targets> | ||||
|  | ||||
|     <rules> | ||||
|         <logger name="Microsoft.*" finalMinLevel="Warn" /> | ||||
|         <logger name="System.Net.Http.HttpClient.*" finalMinLevel="Warn" /> | ||||
|         <logger name="*" minlevel="Debug" writeTo="consoleLog, appLog"/> | ||||
|     </rules> | ||||
| </nlog> | ||||
							
								
								
									
										32
									
								
								MesaFabApproval.API/nLog.config
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								MesaFabApproval.API/nLog.config
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| <?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="asyncFile" 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="System.Net.Http.HttpClient.*" finalMinLevel="Warn" /> | ||||
|         <logger name="*" minlevel="Info" writeTo="consoleLog, appLog" /> | ||||
|     </rules> | ||||
| </nlog> | ||||
							
								
								
									
										26
									
								
								MesaFabApproval.Client/App.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								MesaFabApproval.Client/App.razor
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										70
									
								
								MesaFabApproval.Client/Layout/MainLayout.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								MesaFabApproval.Client/Layout/MainLayout.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| @inherits LayoutComponentBase | ||||
|  | ||||
| @inject MesaFabApprovalAuthStateProvider authStateProvider | ||||
| @inject IConfiguration Configuration | ||||
| @inject IMemoryCache cache | ||||
| @inject NavigationManager navManager | ||||
|  | ||||
| <MudThemeProvider /> | ||||
| <MudDialogProvider /> | ||||
| <MudSnackbarProvider /> | ||||
|  | ||||
| <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> | ||||
|                     </MudNavGroup> | ||||
|                     <MudNavLink OnClick="@(() => GoTo(""))" Icon="@Icons.Material.Filled.Dashboard">Dashboard</MudNavLink> | ||||
|                     <MudNavLink OnClick="@(() => GoTo("mrb/all"))" Icon="@Icons.Material.Filled.Ballot">MRB</MudNavLink> | ||||
|                 } | ||||
|             </MudNavMenu> | ||||
|         </MudDrawer> | ||||
|         <div style="display: flex; flex-flow: column; height: 100%;"> | ||||
|             <MudMainContent Style="@($"background:{Colors.Grey.Lighten2}; 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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										77
									
								
								MesaFabApproval.Client/Layout/MainLayout.razor.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								MesaFabApproval.Client/Layout/MainLayout.razor.css
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								MesaFabApproval.Client/MesaFabApproval.Client.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								MesaFabApproval.Client/MesaFabApproval.Client.csproj
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <TargetFramework>net8.0</TargetFramework> | ||||
|     <Nullable>enable</Nullable> | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Blazored.SessionStorage" Version="2.4.0" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.6" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.6" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.6" 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.6" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" /> | ||||
|     <PackageReference Include="MudBlazor" Version="6.20.0" /> | ||||
|     <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.1" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|     <ItemGroup> | ||||
|         <ProjectReference Include="..\MesaFabApproval.Shared\MesaFabApproval.Shared.csproj" /> | ||||
|     </ItemGroup> | ||||
|  | ||||
| </Project> | ||||
							
								
								
									
										64
									
								
								MesaFabApproval.Client/Pages/AuthenticatedRedirect.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								MesaFabApproval.Client/Pages/AuthenticatedRedirect.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| @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 = System.Net.WebUtility.UrlDecode(redirectPath); | ||||
|             } | ||||
|  | ||||
|             if (!string.IsNullOrWhiteSpace(_jwt) && !string.IsNullOrWhiteSpace(_refreshToken)) { | ||||
|                 await authService.SetTokens(_jwt, _refreshToken); | ||||
|  | ||||
|                 ClaimsPrincipal principal = authService.GetClaimsPrincipalFromJwt(_jwt); | ||||
|  | ||||
|                 string loginId = userService.GetLoginIdFromClaimsPrincipal(principal); | ||||
|  | ||||
|                 await authService.SetLoginId(loginId); | ||||
|                 await authService.SetTokens(_jwt, _refreshToken); | ||||
|  | ||||
|                 User? user = await userService.GetUserByLoginId(loginId); | ||||
|                 await authService.SetCurrentUser(user); | ||||
|  | ||||
|                 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"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										69
									
								
								MesaFabApproval.Client/Pages/Components/Comments.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								MesaFabApproval.Client/Pages/Components/Comments.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| @inject ISnackbar snackbar | ||||
|  | ||||
| <MudDialog> | ||||
|     <DialogContent> | ||||
|         <MudPaper Class="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 async Task OnParametersSetAsync() { | ||||
|         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.Cancel(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										166
									
								
								MesaFabApproval.Client/Pages/Components/MRBActionForm.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								MesaFabApproval.Client/Pages/Components/MRBActionForm.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,166 @@ | ||||
| @inject IMRBService mrbService | ||||
| @inject ISnackbar snackbar | ||||
|  | ||||
| <MudDialog> | ||||
|     <DialogContent> | ||||
|         <MudPaper Class="p-2"> | ||||
|             <MudForm @bind-Errors="@errors"> | ||||
|                 <MudSelect T="string" | ||||
|                            Label="Action" | ||||
|                            Required="true" | ||||
|                            RequiredError="You must select an action!" | ||||
|                            @bind-Value="@mrbAction.Action" | ||||
|                            Text="@mrbAction.Action"> | ||||
|                     <MudSelectItem Value="@("Block")" /> | ||||
|                     <MudSelectItem Value="@("Convert")" /> | ||||
|                     <MudSelectItem Value="@("Other")" /> | ||||
|                     <MudSelectItem Value="@("Recall")" /> | ||||
|                     <MudSelectItem Value="@("Scrap")" /> | ||||
|                     <MudSelectItem Value="@("Unblock")" /> | ||||
|                     <MudSelectItem Value="@("Waiver")" /> | ||||
|                 </MudSelect> | ||||
|                 <MudTextField T="string" | ||||
|                               Label="Customer / Vendor" | ||||
|                               Required="true" | ||||
|                               RequiredError="Customer / Vendor required!" | ||||
|                               @bind-Value="@mrbAction.Customer" | ||||
|                               Text="@mrbAction.Customer" /> | ||||
|                 <MudTextField T="int" | ||||
|                               Label="Qty" | ||||
|                               Required="true" | ||||
|                               RequiredError="You must supply a quantity!" | ||||
|                               @bind-Value=mrbAction.Quantity | ||||
|                               Text="@mrbAction.Quantity.ToString()" /> | ||||
|                 <MudTextField T="string" | ||||
|                               Label="Part Number" | ||||
|                               Required="true" | ||||
|                               RequiredError="Part number required!" | ||||
|                               @bind-Value="@mrbAction.PartNumber" | ||||
|                               Text="@mrbAction.PartNumber" /> | ||||
|                 <MudTextField T="string" | ||||
|                               Label="Batch Number / Lot Number" | ||||
|                               Required="true" | ||||
|                               RequiredError="Batch number / Lot number required!" | ||||
|                               @bind-Value="@mrbAction.LotNumber" | ||||
|                               Text="@mrbAction.LotNumber" /> | ||||
|             </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 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) { | ||||
|             mrbAction = new() { | ||||
|                     Action = "", | ||||
|                     Customer = "", | ||||
|                     Quantity = 0, | ||||
|                     PartNumber = "", | ||||
|                     LotNumber = "", | ||||
|                     MRBNumber = 0 | ||||
|                 }; | ||||
|         } | ||||
|  | ||||
|         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"); | ||||
|         return actionIsValid && !string.IsNullOrWhiteSpace(mrbAction.Customer) && | ||||
|             !string.IsNullOrWhiteSpace(mrbAction.PartNumber) && | ||||
|             !string.IsNullOrWhiteSpace(mrbAction.LotNumber); | ||||
|     } | ||||
|  | ||||
|     private async void SaveMRBAction() { | ||||
|         processingSave = true; | ||||
|         try { | ||||
|             if (!FormIsValid()) throw new Exception("You must complete the form before saving!"); | ||||
|  | ||||
|             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); | ||||
|                 } | ||||
|             } else { | ||||
|                 snackbar.Add("MRB action saved", Severity.Success); | ||||
|             } | ||||
|  | ||||
|             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); | ||||
|  | ||||
|             MudDialog.Close(DialogResult.Ok<MRBAction>(null)); | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
|         } | ||||
|         processingDelete = false; | ||||
|     } | ||||
|  | ||||
|     private void Cancel() { | ||||
|         MudDialog.Cancel(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| @attribute [AllowAnonymous] | ||||
| @inject NavigationManager Navigation | ||||
|  | ||||
| @code { | ||||
|     protected override void OnInitialized() { | ||||
|         Navigation.NavigateTo("login"); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										77
									
								
								MesaFabApproval.Client/Pages/Dashboard.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								MesaFabApproval.Client/Pages/Dashboard.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| @page "/" | ||||
| @page "/Dashboard" | ||||
| @inject MesaFabApprovalAuthStateProvider stateProvider | ||||
| @inject IApprovalService approvalService | ||||
| @inject IMemoryCache cache | ||||
| @inject NavigationManager navigationManager | ||||
| @inject ISnackbar snackbar | ||||
|  | ||||
| <PageTitle>Dashboard</PageTitle> | ||||
|  | ||||
| <MudPaper Class="p-2 m-2" MinWidth="100%"> | ||||
|     <MudText Typo="Typo.h3" Align="Align.Center">Dashboard</MudText> | ||||
| </MudPaper> | ||||
|  | ||||
| @if (stateProvider.CurrentUser is not null && approvalList is not null) { | ||||
|     <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 InitialDirection="SortDirection.Descending" SortBy="new Func<Approval,object>(x=>x.IssueID)"> | ||||
|                         Issue ID | ||||
|                     </MudTableSortLabel> | ||||
|                 </MudTh> | ||||
|                 <MudTh> | ||||
|                     <MudTableSortLabel InitialDirection="SortDirection.Descending" 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 InitialDirection="SortDirection.Descending" SortBy="new Func<Approval,object>(x=>x.Step)"> | ||||
|                         Step | ||||
|                     </MudTableSortLabel> | ||||
|                 </MudTh> | ||||
|             </HeaderContent> | ||||
|             <RowTemplate> | ||||
|                 <MudTd DataLabel="Issue ID"> | ||||
|                     <MudLink OnClick="@(() => GoTo($"/mrb/{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> | ||||
| } | ||||
|  | ||||
| @code { | ||||
|     private IEnumerable<Approval> approvalList = new List<Approval>(); | ||||
|  | ||||
|     protected async override Task OnParametersSetAsync() { | ||||
|         try { | ||||
|             if (stateProvider.CurrentUser is not null) | ||||
|                 approvalList = (await approvalService.GetApprovalsForUserId(stateProvider.CurrentUser.UserID, true)).ToList(); | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add($"Unable to fetch your outstanding approvals, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void GoTo(string page) { | ||||
|         cache.Set("redirectUrl", page); | ||||
|         navigationManager.NavigateTo(page); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										84
									
								
								MesaFabApproval.Client/Pages/Login.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								MesaFabApproval.Client/Pages/Login.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | ||||
| @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> | ||||
|     </MudForm> | ||||
| </MudPaper> | ||||
|  | ||||
| @code { | ||||
|     [Parameter] | ||||
|     public string? redirectUrl { get; set; } | ||||
|     [Parameter] | ||||
|     public string? redirectUrlSub { get; set; } | ||||
|     private bool success; | ||||
|     private bool processing = 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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										113
									
								
								MesaFabApproval.Client/Pages/MRBAll.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								MesaFabApproval.Client/Pages/MRBAll.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,113 @@ | ||||
| @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.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="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(); | ||||
|             } | ||||
|         } 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.ToLower())) | ||||
|             return true; | ||||
|         if (mrb.OriginatorName.ToLower().Contains(searchString.ToLower())) | ||||
|             return true; | ||||
|         if (mrb.MRBNumber.ToString().Contains(searchString)) | ||||
|             return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     private void GoTo(string page) { | ||||
|         cache.Set("redirectUrl", page); | ||||
|         navigationManager.NavigateTo(page); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										996
									
								
								MesaFabApproval.Client/Pages/MRBSingle.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										996
									
								
								MesaFabApproval.Client/Pages/MRBSingle.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,996 @@ | ||||
| @page "/mrb/{mrbNumber}" | ||||
| @page "/mrb/new" | ||||
| @using System.Text | ||||
| @inject IMRBService mrbService | ||||
| @inject ISnackbar snackbar | ||||
| @inject IDialogService dialogService | ||||
| @inject NavigationManager navigationManager | ||||
| @inject MesaFabApprovalAuthStateProvider authStateProvider | ||||
| @inject IUserService userService | ||||
| @inject IMemoryCache cache | ||||
| @inject IConfiguration config | ||||
| @inject IApprovalService approvalService | ||||
|  | ||||
| @if (mrbNumber is not null) { | ||||
|     <PageTitle>MRB @mrbNumber</PageTitle> | ||||
|  | ||||
|     <MudPaper Class="p-2 m-2 d-flex flex-row justify-content-between"> | ||||
|         <MudIconButton Icon="@Icons.Material.Filled.ChevronLeft" | ||||
|                        Variant="Variant.Outlined" | ||||
|                        Color="Color.Dark" | ||||
|                        OnClick="@ReturnToAllMrbs" | ||||
|                        Size="Size.Large" /> | ||||
|         <MudText Typo="Typo.h3" Align="Align.Center">MRB @mrbNumber</MudText> | ||||
|         <MudPaper Height="100%" Width="0.1%" Square="true" /> | ||||
|     </MudPaper> | ||||
| } | ||||
|  | ||||
| @if (mrb is not null) { | ||||
|     <MudTimeline Class="mt-2 pt-2" TimelineOrientation="TimelineOrientation.Horizontal" | ||||
|                  TimelinePosition="TimelinePosition.Bottom"> | ||||
|         @for (int i = 0; i < MRB.Stages.Length; i++) { | ||||
|             Color color; | ||||
|             if (mrb.StageNo > i || mrb.StageNo == (MRB.Stages.Length - 1)) { | ||||
|                 color = Color.Success; | ||||
|             } else if (mrb.StageNo == i) { | ||||
|                 color = Color.Info; | ||||
|             } else { | ||||
|                 color = Color.Dark; | ||||
|             } | ||||
|  | ||||
|             string stageName = MRB.Stages[i]; | ||||
|  | ||||
|             <MudTimelineItem Color="@color" Variant="Variant.Filled"> | ||||
|                 <MudText Align="Align.Center" Color="@color">@stageName</MudText> | ||||
|             </MudTimelineItem> | ||||
|         } | ||||
|     </MudTimeline> | ||||
|  | ||||
|     bool mrbIsSubmitted = mrb.SubmittedDate > DateTime.MinValue; | ||||
|     bool mrbReadyToSubmit = mrbIsReadyToSubmit(); | ||||
|     bool userIsApprover = currentUserIsApprover(); | ||||
|  | ||||
|     if (!mrbIsSubmitted || (!mrbIsSubmitted && mrbReadyToSubmit) || (mrbIsSubmitted && userIsApprover)) | ||||
|     { | ||||
|         <MudPaper Outlined="true" | ||||
|         Class="p-2 m-2 d-flex flex-wrap gap-3 justify-content-center align-content-center" | ||||
|         Elevation="10"> | ||||
|             @if (!mrbIsSubmitted) | ||||
|             { | ||||
|                 <MudButton Variant="Variant.Filled" | ||||
|                 Color="Color.Tertiary" | ||||
|                 OnClick=SaveMRB> | ||||
|                     @if (saveInProcess) | ||||
|                     { | ||||
|                         <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|                         <MudText>Processing</MudText> | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         <MudText>Save</MudText> | ||||
|                     } | ||||
|                 </MudButton> | ||||
|             } | ||||
|             @if (!mrbIsSubmitted && mrbReadyToSubmit) | ||||
|             { | ||||
|                 <MudButton Variant="Variant.Filled" | ||||
|                 Color="Color.Tertiary" | ||||
|                 OnClick=SubmitMRBForApproval> | ||||
|                     @if (submitInProcess) | ||||
|                     { | ||||
|                         <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|                         <MudText>Processing</MudText> | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         <MudText>Submit for Approval</MudText> | ||||
|                     } | ||||
|                 </MudButton> | ||||
|             } | ||||
|             @if (mrbIsSubmitted && userIsApprover) | ||||
|             { | ||||
|                 <MudButton Variant="Variant.Filled" | ||||
|                 Color="Color.Tertiary" | ||||
|                 OnClick=ApproveMRB> | ||||
|                     Approve | ||||
|                 </MudButton> | ||||
|                 <MudButton Variant="Variant.Filled" | ||||
|                 Color="Color.Tertiary" | ||||
|                 OnClick=DenyMRB> | ||||
|                     Deny | ||||
|                 </MudButton> | ||||
|             } | ||||
|         </MudPaper> | ||||
|     } | ||||
|  | ||||
|     <MudPaper Outlined="true" | ||||
|               Class="p-2 m-2 d-flex flex-wrap gap-3 justify-content-center align-content-start" | ||||
|               Elevation="10"> | ||||
|         <MudTextField @bind-Value=mrb.MRBNumber | ||||
|                       Text="@mrb.MRBNumber.ToString()" | ||||
|                       T="int" | ||||
|                       Disabled="true" | ||||
|                       Label="MRB#" | ||||
|                       Variant="Variant.Outlined" /> | ||||
|         <MudTextField @bind-Value=mrb.Title | ||||
|                       Text="@mrb.Title" | ||||
|                       Disabled="@(mrb.SubmittedDate > DateTime.MinValue)" | ||||
|                       T="string" | ||||
|                       AutoGrow | ||||
|                       Variant="Variant.Outlined" | ||||
|                       Label="Title" /> | ||||
|  | ||||
|         <MudTextField Disabled="true" | ||||
|                       T="string" | ||||
|                       Value="@DateTimeUtilities.GetDateAsStringMinDefault(mrb.SubmittedDate)" | ||||
|                       Label="Submit Date" | ||||
|                       Variant="Variant.Outlined" /> | ||||
|         <MudTextField Disabled="true" | ||||
|                       Label="Approval Date" | ||||
|                       Variant="Variant.Outlined" | ||||
|                       Value="@DateTimeUtilities.GetDateAsStringMaxDefault(mrb.ApprovalDate)" | ||||
|                       T="string" /> | ||||
|         <MudTextField Disabled="true" | ||||
|                       Label="Closed Date" | ||||
|                       Variant="Variant.Outlined" | ||||
|                       Value="@DateTimeUtilities.GetDateAsStringMaxDefault(mrb.CloseDate)" | ||||
|                       T="string" /> | ||||
|         @if (mrb is not null && mrb.CancelDate < DateTime.MaxValue) { | ||||
|             <MudTextField Disabled="true" | ||||
|                           Label="Canceled Date" | ||||
|                           Variant="Variant.Outlined" | ||||
|                           Value="@DateTimeUtilities.GetDateAsStringMaxDefault(mrb.CancelDate)" | ||||
|                           T="string" /> | ||||
|         } | ||||
|  | ||||
|         <MudTextField Disabled="@(mrb.SubmittedDate > DateTime.MinValue)" | ||||
|                       @bind-Value=mrb.IssueDescription | ||||
|                       Text="@mrb.IssueDescription" | ||||
|                       AutoGrow | ||||
|                       Variant="Variant.Outlined" | ||||
|                       Label="Description" /> | ||||
|         <MudSelect T="string" | ||||
|                    Label="Originator" | ||||
|                    Variant="Variant.Outlined" | ||||
|                    AnchorOrigin="Origin.BottomCenter" | ||||
|                    Disabled=@(mrb.SubmittedDate > DateTime.MinValue) | ||||
|                    @bind-Value=mrb.OriginatorName | ||||
|                    Text="@mrb.OriginatorName"> | ||||
|             @foreach (User user in allActiveUsers) { | ||||
|                 <MudSelectItem T="string" Value="@($"{user.FirstName} {user.LastName}")" /> | ||||
|             } | ||||
|         </MudSelect> | ||||
|         <MudSelect T="string" | ||||
|                    Label="Department" | ||||
|                    Variant="Variant.Outlined" | ||||
|                    AnchorOrigin="Origin.BottomCenter" | ||||
|                    Disabled=@(mrb.SubmittedDate > DateTime.MinValue) | ||||
|                    @bind-Value=mrb.Department | ||||
|                    Text="@mrb.Department"> | ||||
|             <MudSelectItem Value="@("Production")" /> | ||||
|             <MudSelectItem Value="@("Engineering")" /> | ||||
|             <MudSelectItem Value="@("Materials")" /> | ||||
|             <MudSelectItem Value="@("Facilities")" /> | ||||
|             <MudSelectItem Value="@("Maintenance")" /> | ||||
|             <MudSelectItem Value="@("Quality")" /> | ||||
|         </MudSelect> | ||||
|         <MudSelect T="string" | ||||
|                    Label="Process" | ||||
|                    Variant="Variant.Outlined" | ||||
|                    AnchorOrigin="Origin.BottomCenter" | ||||
|                    Disabled=@(mrb.SubmittedDate > DateTime.MinValue) | ||||
|                    @bind-Value=mrb.Process | ||||
|                    Text="@mrb.Process"> | ||||
|             <MudSelectItem Value="@("Receiving")" /> | ||||
|             <MudSelectItem Value="@("Kitting")" /> | ||||
|             <MudSelectItem Value="@("Cleans")" /> | ||||
|             <MudSelectItem Value="@("Reactor")" /> | ||||
|             <MudSelectItem Value="@("Metrology")" /> | ||||
|             <MudSelectItem Value="@("Final QA")" /> | ||||
|             <MudSelectItem Value="@("Packaging")" /> | ||||
|             <MudSelectItem Value="@("Shipping")" /> | ||||
|             <MudSelectItem Value="@("N/A")" /> | ||||
|         </MudSelect> | ||||
|         <MudTextField Disabled="@(mrb.SubmittedDate > DateTime.MinValue)" | ||||
|                       @bind-Value=mrb.NumberOfLotsAffected | ||||
|                       Text="@mrb.NumberOfLotsAffected.ToString()" | ||||
|                       Variant="Variant.Outlined" | ||||
|                       Label="Total Quantity" /> | ||||
|         <MudTextField Disabled="@(mrb.SubmittedDate > DateTime.MinValue)" | ||||
|                       @bind-Value=mrb.Val | ||||
|                       Text="@mrb.Val" | ||||
|                       Variant="Variant.Outlined" | ||||
|                       Label="Value" /> | ||||
|         <MudTextField Disabled="@(mrb.SubmittedDate > DateTime.MinValue)" | ||||
|                       @bind-Value=mrb.RMANo | ||||
|                       Text="@mrb.RMANo.ToString()" | ||||
|                       Variant="Variant.Outlined" | ||||
|                       Label="RMA#" /> | ||||
|         <MudPaper Outlined="true" Class="p-2"> | ||||
|             <MudCheckBox Disabled="@(mrb.SubmittedDate > DateTime.MinValue)" | ||||
|                          @bind-Value=mrb.CustomerImpacted | ||||
|                          Color="Color.Tertiary" | ||||
|                          Label="Customer Impacted?" | ||||
|                          LabelPosition="LabelPosition.Start" /> | ||||
|         </MudPaper> | ||||
|         <MudTextField Disabled="@(mrb.SubmittedDate > DateTime.MinValue)" | ||||
|                       @bind-Value=mrb.PCRBNo | ||||
|                       Text="@mrb.PCRBNo.ToString()" | ||||
|                       Variant="Variant.Outlined" | ||||
|                       Label="PCRB#" /> | ||||
|         <MudPaper Outlined="true" Class="p-2"> | ||||
|             <MudCheckBox Disabled="@(mrb.SubmittedDate > DateTime.MinValue)" | ||||
|                          @bind-Value=mrb.SpecsImpacted | ||||
|                          Color="Color.Tertiary" | ||||
|                          Label="Specs Impacted?" | ||||
|                          LabelPosition="LabelPosition.Start" /> | ||||
|         </MudPaper> | ||||
|     </MudPaper> | ||||
| } | ||||
|  | ||||
| @if (mrb is not null && mrb.MRBNumber > 0) { | ||||
|     <MudPaper Outlined="true" | ||||
|               Class="p-2 m-2 d-flex flex-column justify-center"> | ||||
|         <MudText Typo="Typo.h4" Align="Align.Center">Supporting Documents</MudText> | ||||
|         <MudDivider DividerType="DividerType.Middle" Class="my-2" /> | ||||
|         @if (!(mrb.SubmittedDate > DateTime.MinValue)) { | ||||
|             <MudFileUpload T="IBrowserFile" OnFilesChanged="AddAttachments" Class="centered-upload"> | ||||
|                 <ButtonTemplate> | ||||
|                     <MudButton HtmlTag="label" | ||||
|                                Variant="Variant.Filled" | ||||
|                                Color="Color.Tertiary" | ||||
|                                style="margin: auto;" | ||||
|                                StartIcon="@Icons.Material.Filled.AttachFile" | ||||
|                                for="@context.Id"> | ||||
|                         Upload Document | ||||
|                     </MudButton> | ||||
|                 </ButtonTemplate> | ||||
|             </MudFileUpload> | ||||
|         } | ||||
|  | ||||
|         @if (mrbAttachments is not null) { | ||||
|             <MudTable Items="@mrbAttachments" | ||||
|                       Class="m-2" | ||||
|                       Striped="true" | ||||
|                       Filter="new Func<MRBAttachment, bool>(FilterFuncForMRBAttachmentTable)" | ||||
|                       SortLabel="Sort By" | ||||
|                       Hover="true"> | ||||
|                 <ToolBarContent> | ||||
|                     <MudSpacer /> | ||||
|                     <MudTextField @bind-Value="attachmentSearchString" | ||||
|                                   Placeholder="Search" | ||||
|                                   Adornment="Adornment.Start" | ||||
|                                   AdornmentIcon="@Icons.Material.Filled.Search" | ||||
|                                   IconSize="Size.Medium" | ||||
|                                   Class="mt-0" /> | ||||
|                 </ToolBarContent> | ||||
|                 <HeaderContent> | ||||
|                     <MudTh> | ||||
|                         <MudTableSortLabel SortBy="new Func<MRBAttachment, object>(x=>x.FileName)"> | ||||
|                             Name | ||||
|                         </MudTableSortLabel> | ||||
|                     </MudTh> | ||||
|                     <MudTh> | ||||
|                         <MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<MRBAttachment,object>(x=>x.UploadDate)"> | ||||
|                             Upload Date | ||||
|                         </MudTableSortLabel> | ||||
|                     </MudTh> | ||||
|                 </HeaderContent> | ||||
|                 <RowTemplate> | ||||
|                     <MudTd DataLabel="Name"> | ||||
|                         <a href="@(@$"{config["FabApprovalApiBaseUrl"]}/mrb/attachmentFile?path={context.Path}&fileName={context.FileName}")" | ||||
|                            download="@(context.FileName)" | ||||
|                            target="_top"> | ||||
|                             @context.FileName | ||||
|                         </a> | ||||
|                     </MudTd> | ||||
|                     <MudTd DataLabel="Upload Date">@context.UploadDate.ToString("yyyy-MM-dd HH:mm")</MudTd> | ||||
|                     @if (mrb is not null && !(mrb.SubmittedDate > DateTime.MinValue)) { | ||||
|                         <MudTd> | ||||
|                             <MudButton Color="Color.Secondary" | ||||
|                                        Variant="Variant.Filled" | ||||
|                                        OnClick="@((e) => DeleteAttachment(context))"> | ||||
|                                 @if (deleteActionInProcess) { | ||||
|                                     <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|                                     <MudText>Deleting</MudText> | ||||
|                                 } else { | ||||
|                                     <MudText>Delete</MudText> | ||||
|                                 } | ||||
|                             </MudButton> | ||||
|                         </MudTd> | ||||
|                     } | ||||
|                 </RowTemplate> | ||||
|             </MudTable> | ||||
|         } | ||||
|  | ||||
|         <MudPaper Outlined="true" | ||||
|                   Class="p-2 m-2 d-flex flex-column justify-center"> | ||||
|             <MudText Typo="Typo.h4" Align="Align.Center">Actions</MudText> | ||||
|             <MudDivider DividerType="DividerType.Middle" Class="my-2" /> | ||||
|             @if (!(mrb.SubmittedDate > DateTime.MinValue)) { | ||||
|                 <MudButton Variant="Variant.Filled" | ||||
|                            Color="Color.Tertiary" | ||||
|                            Class="object-center flex-grow-0" | ||||
|                            Style="max-width: 200px; margin: auto;" | ||||
|                            OnClick="@((e) => CreateNewAction())"> | ||||
|                     Create New Action | ||||
|                 </MudButton> | ||||
|             } | ||||
|  | ||||
|             @if (mrbActions is not null) { | ||||
|                 <MudTable Items="@mrbActions" | ||||
|                           Class="m-2" | ||||
|                           Striped="true" | ||||
|                           Filter="new Func<MRBAction, bool>(FilterFuncForMRBActionTable)" | ||||
|                           SortLabel="Sort By" | ||||
|                           Hover="true"> | ||||
|                     <ToolBarContent> | ||||
|                         <MudSpacer /> | ||||
|                         <MudTextField @bind-Value="actionSearchString" | ||||
|                                       Placeholder="Search" | ||||
|                                       Adornment="Adornment.Start" | ||||
|                                       AdornmentIcon="@Icons.Material.Filled.Search" | ||||
|                                       IconSize="Size.Medium" | ||||
|                                       Class="mt-0" /> | ||||
|                     </ToolBarContent> | ||||
|                     <HeaderContent> | ||||
|                         <MudTh> | ||||
|                             <MudTableSortLabel InitialDirection="SortDirection.Ascending" SortBy="new Func<MRBAction,object>(x=>x.Action)"> | ||||
|                                 Action | ||||
|                             </MudTableSortLabel> | ||||
|                         </MudTh> | ||||
|                         <MudTh> | ||||
|                             <MudTableSortLabel SortBy="new Func<MRBAction,object>(x=>x.Customer)"> | ||||
|                                 Customer | ||||
|                             </MudTableSortLabel> | ||||
|                         </MudTh> | ||||
|                         <MudTh> | ||||
|                             <MudTableSortLabel SortBy="new Func<MRBAction,object>(x=>x.Quantity)"> | ||||
|                                 Qty | ||||
|                             </MudTableSortLabel> | ||||
|                         </MudTh> | ||||
|                         <MudTh> | ||||
|                             <MudTableSortLabel SortBy="new Func<MRBAction,object>(x=>x.PartNumber)"> | ||||
|                                 Part Number | ||||
|                             </MudTableSortLabel> | ||||
|                         </MudTh> | ||||
|                         <MudTh> | ||||
|                             <MudTableSortLabel SortBy="new Func<MRBAction,object>(x=>x.LotNumber)"> | ||||
|                                 Batch Number / Lot Number | ||||
|                             </MudTableSortLabel> | ||||
|                         </MudTh> | ||||
|                         <MudTh> | ||||
|                             <MudTableSortLabel SortBy="new Func<MRBAction, object>(x=>x.AssignedDate)"> | ||||
|                                 Assigned Date | ||||
|                             </MudTableSortLabel> | ||||
|                         </MudTh> | ||||
|                         <MudTh> | ||||
|                             <MudTableSortLabel SortBy="new Func<MRBAction, object>(x=>x.CompletedDate)"> | ||||
|                                 Completed Date | ||||
|                             </MudTableSortLabel> | ||||
|                         </MudTh> | ||||
|                         <MudTh> | ||||
|                             <MudTableSortLabel InitialDirection="SortDirection.Ascending" SortBy="new Func<MRBAction,object>(x=>x.CompletedByUser?.GetFullName() ?? | ||||
|                                                                                                                         x.CompletedByUserID.ToString())"> | ||||
|                                 Completed By | ||||
|                             </MudTableSortLabel> | ||||
|                         </MudTh> | ||||
|                     </HeaderContent> | ||||
|                     <RowTemplate> | ||||
|                         <MudTd DataLabel="Action">@context.Action</MudTd> | ||||
|                         <MudTd DataLabel="Customer">@context.Customer</MudTd> | ||||
|                         <MudTd DataLabel="Qty">@context.Quantity</MudTd> | ||||
|                         <MudTd DataLabel="Part Number">@context.PartNumber</MudTd> | ||||
|                         <MudTd DataLabel="Batch Number / Lot Number">@context.LotNumber</MudTd> | ||||
|                         <MudTd DataLabel="Assigned Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.AssignedDate)</MudTd> | ||||
|                         <MudTd DataLabel="Completed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CompletedDate)</MudTd> | ||||
|                         <MudTd DataLabel="Completed By">@context.CompletedByUser?.GetFullName()</MudTd> | ||||
|                         @if (mrb is not null && !(mrb.SubmittedDate > DateTime.MinValue)) { | ||||
|                             <MudTd> | ||||
|                                 <MudButton Variant="Variant.Filled" | ||||
|                                            Color="Color.Tertiary" | ||||
|                                            OnClick="@((e) => EditAction(context))"> | ||||
|                                     Edit | ||||
|                                 </MudButton> | ||||
|                                 <MudButton Color="Color.Secondary" | ||||
|                                            Variant="Variant.Filled" | ||||
|                                            OnClick="@((e) => DeleteAction(context))"> | ||||
|                                     @if (deleteActionInProcess) { | ||||
|                                         <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|                                         <MudText>Deleting</MudText> | ||||
|                                     } else { | ||||
|                                         <MudText>Delete</MudText> | ||||
|                                     } | ||||
|                                 </MudButton> | ||||
|                             </MudTd> | ||||
|                         } | ||||
|                         @if (currentUser is not null && taskApprovals.Where(t => t.UserID == currentUser.UserID && t.TaskID == context.ActionID).ToList().Count() > 0 && | ||||
|                 context.CompletedDate >= DateTime.MaxValue) { | ||||
|                             <MudTd> | ||||
|                                 <MudButton Variant="Variant.Filled" | ||||
|                                            Color="Color.Tertiary" | ||||
|                                            OnClick="@((e) => CompleteAction(context))"> | ||||
|                                     Mark Complete | ||||
|                                 </MudButton> | ||||
|                             </MudTd> | ||||
|                         } | ||||
|                     </RowTemplate> | ||||
|                     <PagerContent> | ||||
|                         <MudTablePager /> | ||||
|                     </PagerContent> | ||||
|                 </MudTable> | ||||
|             } | ||||
|         </MudPaper> | ||||
|  | ||||
|         <MudPaper Outlined="true" | ||||
|                   Class="p-2 m-2 d-flex flex-column justify-center"> | ||||
|             <MudText Typo="Typo.h4" Align="Align.Center">Approvals</MudText> | ||||
|  | ||||
|             @if (nonTaskApprovals is not null) { | ||||
|                 <MudTable Items="@nonTaskApprovals" | ||||
|                           Class="m-2" | ||||
|                           Striped="true"> | ||||
|                     <HeaderContent> | ||||
|                         <MudTh>Role</MudTh> | ||||
|                         <MudTh>Approver Name</MudTh> | ||||
|                         <MudTh>Status</MudTh> | ||||
|                         <MudTh>Disposition Date</MudTh> | ||||
|                         <MudTh>Comments</MudTh> | ||||
|                     </HeaderContent> | ||||
|                     <RowTemplate> | ||||
|                         <MudTd DataLabel="Role">@context.SubRoleCategoryItem</MudTd> | ||||
|                         <MudTd DataLabel="Approver Name">@context.User?.GetFullName()</MudTd> | ||||
|                         <MudTd DataLabel="Status">@context.StatusMessage</MudTd> | ||||
|                         <MudTd DataLabel="Disposition Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CompletedDate)</MudTd> | ||||
|                         <MudTd DataLabel="Comments">@context.Comments</MudTd> | ||||
|                     </RowTemplate> | ||||
|                 </MudTable> | ||||
|             } | ||||
|         </MudPaper> | ||||
|     </MudPaper> | ||||
| } | ||||
|  | ||||
| <MudOverlay Visible=processing DarkBackground="true" AutoClose="false"> | ||||
|     <MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" /> | ||||
| </MudOverlay> | ||||
|  | ||||
| @code { | ||||
|     [Parameter] | ||||
|     public string? mrbNumber { get; set; } | ||||
|     private int mrbNumberInt = 0; | ||||
|     private MRB? mrb { get; set; } | ||||
|     private User? currentUser = null; | ||||
|     private int currentUserId = 0; | ||||
|     private IEnumerable<User> allActiveUsers = new List<User>(); | ||||
|     private IEnumerable<MRBAction> mrbActions = new List<MRBAction>(); | ||||
|     private IEnumerable<MRBAttachment> mrbAttachments = new List<MRBAttachment>(); | ||||
|     private IEnumerable<Approval> mrbApprovals = new List<Approval>(); | ||||
|     private IEnumerable<Approval> nonTaskApprovals = new List<Approval>(); | ||||
|     private IEnumerable<Approval> taskApprovals = new List<Approval>(); | ||||
|     private bool processing = false; | ||||
|     private bool saveInProcess = false; | ||||
|     private bool submitInProcess = false; | ||||
|     private bool approvalInProcess = false; | ||||
|     private bool taskApprovalInProcess = false; | ||||
|     private bool denialInProcess = false; | ||||
|     private bool deleteActionInProcess = false; | ||||
|     private bool deleteAttachmentInProcess = false; | ||||
|     private string actionSearchString = ""; | ||||
|     private string attachmentSearchString = ""; | ||||
|     private string currentUrl = ""; | ||||
|  | ||||
|     protected override async Task OnParametersSetAsync() { | ||||
|         processing = true; | ||||
|         try { | ||||
|             if (allActiveUsers is null) allActiveUsers = await userService.GetAllActiveUsers(); | ||||
|             if (authStateProvider is not null) currentUser = authStateProvider.CurrentUser; | ||||
|             if (navigationManager is not null) currentUrl = navigationManager.Uri; | ||||
|  | ||||
|             if (!string.IsNullOrWhiteSpace(mrbNumber) && Int32.TryParse(mrbNumber, out mrbNumberInt)) { | ||||
|                 mrb = await mrbService.GetMRBById(mrbNumberInt); | ||||
|                 mrbActions = await mrbService.GetMRBActionsForMRB(mrbNumberInt, false); | ||||
|                 mrbAttachments = await mrbService.GetAllAttachmentsForMRB(mrbNumberInt, false); | ||||
|  | ||||
|                 mrbApprovals = await approvalService.GetApprovalsForIssueId(mrbNumberInt, false); | ||||
|  | ||||
|                 nonTaskApprovals = mrbApprovals.Where(a => a.Step < 3).ToList(); | ||||
|                 taskApprovals = mrbApprovals.Where(a => a.Step >= 3).ToList(); | ||||
|             } else { | ||||
|                 mrbNumberInt = 0; | ||||
|                 mrbNumber = ""; | ||||
|                 mrb = new() { Status = "Draft", StageNo = 0 }; | ||||
|                 mrbActions = new List<MRBAction>(); | ||||
|                 mrbAttachments = new List<MRBAttachment>(); | ||||
|                 mrbApprovals = new List<Approval>(); | ||||
|                 nonTaskApprovals = new List<Approval>(); | ||||
|                 taskApprovals = new List<Approval>(); | ||||
|                 if (authStateProvider is not null && authStateProvider.CurrentUser is not null) { | ||||
|                     mrb.OriginatorID = authStateProvider.CurrentUser.UserID; | ||||
|                     mrb.OriginatorName = $"{authStateProvider.CurrentUser.FirstName} {authStateProvider.CurrentUser.LastName}"; | ||||
|                 } | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
|         } | ||||
|         processing = false; | ||||
|     } | ||||
|  | ||||
|     private void ReturnToAllMrbs() { | ||||
|         cache.Set("redirectUrl", $"mrb/all"); | ||||
|         navigationManager.NavigateTo("mrb/all"); | ||||
|     } | ||||
|  | ||||
|     private async void SaveMRB() { | ||||
|         try { | ||||
|             saveInProcess = true; | ||||
|             MRB initialMrb = new MRB() { | ||||
|                     MRBNumber = mrb.MRBNumber, | ||||
|                     Status = mrb.Status, | ||||
|                     StageNo = 0 | ||||
|                 }; | ||||
|             if (mrb is not null) { | ||||
|                 User? originator = allActiveUsers.Where(u => $"{u.FirstName} {u.LastName}".Equals(mrb.OriginatorName)).FirstOrDefault(); | ||||
|                 if (originator is not null) mrb.OriginatorID = originator.UserID; | ||||
|  | ||||
|                 if (mrb.MRBNumber <= 0) { | ||||
|                     await mrbService.CreateNewMRB(mrb); | ||||
|                     mrb = await mrbService.GetMRBByTitle(mrb.Title, true); | ||||
|                     cache.Set("redirectUrl", $"mrb/{mrb.MRBNumber}"); | ||||
|                 } else { | ||||
|                     await mrbService.UpdateMRB(mrb); | ||||
|                 } | ||||
|  | ||||
|                 mrbNumber = mrb.MRBNumber.ToString(); | ||||
|                 mrbNumberInt = mrb.MRBNumber; | ||||
|  | ||||
|                 foreach (MRBAction action in mrbActions) { | ||||
|                     if (action is not null) { | ||||
|                         action.MRBNumber = mrbNumberInt; | ||||
|                         if (action.ActionID > 0) { | ||||
|                             await mrbService.UpdateMRBAction(action); | ||||
|                         } else { | ||||
|                             await mrbService.CreateMRBAction(action); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 mrbActions = await mrbService.GetMRBActionsForMRB(mrbNumberInt, true); | ||||
|             } | ||||
|             saveInProcess = false; | ||||
|             StateHasChanged(); | ||||
|             snackbar.Add($"MRB {mrb.MRBNumber} successfully saved", Severity.Success); | ||||
|  | ||||
|             if (initialMrb.MRBNumber <= 0) | ||||
|                 navigationManager.NavigateTo($"mrb/{mrb.MRBNumber}"); | ||||
|         } catch (Exception ex) { | ||||
|             saveInProcess = false; | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
|         } | ||||
|         saveInProcess = false; | ||||
|     } | ||||
|  | ||||
|     private async void SubmitMRBForApproval() { | ||||
|         submitInProcess = true; | ||||
|         try { | ||||
|             if (mrb is null) throw new Exception("MRB cannot be null"); | ||||
|  | ||||
|             User? originator = allActiveUsers.Where(u => $"{u.FirstName} {u.LastName}".Equals(mrb.OriginatorName)).FirstOrDefault(); | ||||
|             if (originator is not null) mrb.OriginatorID = originator.UserID; | ||||
|  | ||||
|             if (mrb.StageNo == 0) { | ||||
|                 mrb.StageNo++; | ||||
|                 mrb.SubmittedDate = DateTime.Now; | ||||
|                 await mrbService.UpdateMRB(mrb); | ||||
|             } | ||||
|  | ||||
|             foreach (MRBAction action in mrbActions) { | ||||
|                 if (action is not null) { | ||||
|                     action.MRBNumber = mrb.MRBNumber; | ||||
|                     if (action.ActionID > 0) { | ||||
|                         await mrbService.UpdateMRBAction(action); | ||||
|                     } else { | ||||
|                         await mrbService.CreateMRBAction(action); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             mrbActions = await mrbService.GetMRBActionsForMRB(mrbNumberInt, true); | ||||
|  | ||||
|             await mrbService.SubmitForApproval(mrb); | ||||
|  | ||||
|             await mrbService.NotifyNewApprovals(mrb); | ||||
|  | ||||
|             mrbApprovals = await approvalService.GetApprovalsForIssueId(mrb.MRBNumber, true); | ||||
|  | ||||
|             nonTaskApprovals = mrbApprovals.Where(a => a.Step < 3).ToList(); | ||||
|             taskApprovals = mrbApprovals.Where(a => a.Step >= 3).ToList(); | ||||
|  | ||||
|             submitInProcess = false; | ||||
|  | ||||
|             snackbar.Add("MRB submitted for approval", Severity.Success); | ||||
|         } catch (Exception ex) { | ||||
|             submitInProcess = false; | ||||
|             snackbar.Add($"Unable to submit MRB for approval, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|  | ||||
|         StateHasChanged(); | ||||
|     } | ||||
|  | ||||
|     private async void ApproveMRB() { | ||||
|         approvalInProcess = true; | ||||
|         try { | ||||
|             if (mrbApprovals is null || mrbApprovals.Count() <= 0) | ||||
|                 throw new Exception("there are no approvals to approval"); | ||||
|             if (authStateProvider.CurrentUser is null) { | ||||
|                 navigationManager.NavigateTo("login"); | ||||
|                 return; | ||||
|             } | ||||
|             if (mrb is null) throw new Exception("MRB is null"); | ||||
|  | ||||
|             string? comments = ""; | ||||
|  | ||||
|             DialogParameters<Comments> parameters = new DialogParameters<Comments> { { x => x.comments, comments } }; | ||||
|             var dialog = dialogService.Show<Comments>($"Approval Comments", parameters); | ||||
|  | ||||
|             var result = await dialog.Result; | ||||
|  | ||||
|             comments = result.Data.ToString(); | ||||
|  | ||||
|             if (result.Canceled) throw new Exception("you must provide your approval comments"); | ||||
|  | ||||
|             IEnumerable<Approval> approvals = mrbApprovals.Where(a => a.UserID == authStateProvider.CurrentUser.UserID && | ||||
|                                                                       !(a.CompletedDate < DateTime.MaxValue) && | ||||
|                                                                       a.Step == mrb.StageNo); | ||||
|  | ||||
|             foreach (Approval approval in approvals) { | ||||
|                 approval.CompletedDate = DateTime.Now; | ||||
|                 approval.Comments = comments is null ? "" : comments; | ||||
|                 approval.ItemStatus = 1; | ||||
|                 await approvalService.Approve(approval); | ||||
|             } | ||||
|  | ||||
|             IEnumerable<Approval> remainingApprovalsInKind = approvals.Where(a => a.Step == mrb.StageNo && | ||||
|                                                                              a.UserID != authStateProvider.CurrentUser.UserID && | ||||
|                                                                              !(a.CompletedDate < DateTime.MaxValue)); | ||||
|  | ||||
|             if (remainingApprovalsInKind is null || remainingApprovalsInKind.Count() <= 0) { | ||||
|                 mrb.StageNo++; | ||||
|                 if (mrb.StageNo == 3) mrb.ApprovalDate = DateTime.Now; | ||||
|                 await mrbService.UpdateMRB(mrb); | ||||
|  | ||||
|                 if (mrb.StageNo < 3) | ||||
|                     SubmitMRBForApproval(); | ||||
|  | ||||
|                 if (mrb.StageNo == 3) { | ||||
|                     GenerateActionTasks(); | ||||
|  | ||||
|                     string body = $"Your MRB ({mrb.MRBNumber}) has been approved."; | ||||
|  | ||||
|                     MRBNotification notification = new() { | ||||
|                         MRB = mrb, | ||||
|                         Message = body | ||||
|                     }; | ||||
|  | ||||
|                     await mrbService.NotifyOriginator(notification); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             mrbActions = await mrbService.GetMRBActionsForMRB(mrb.MRBNumber, true); | ||||
|  | ||||
|             mrbApprovals = await approvalService.GetApprovalsForIssueId(mrb.MRBNumber, true); | ||||
|             taskApprovals = mrbApprovals.Where(a => a.Step >= 3).ToList(); | ||||
|             nonTaskApprovals = mrbApprovals.Where(a => a.Step < 3).ToList(); | ||||
|  | ||||
|             approvalInProcess = false; | ||||
|  | ||||
|             StateHasChanged(); | ||||
|  | ||||
|             snackbar.Add("Successfully approved", Severity.Success); | ||||
|         } catch (Exception ex) { | ||||
|             approvalInProcess = false; | ||||
|  | ||||
|             snackbar.Add($"Unable to approve, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async void DenyMRB() { | ||||
|         denialInProcess = true; | ||||
|         try { | ||||
|             if (mrbApprovals is null || mrbApprovals.Count() <= 0) | ||||
|                 throw new Exception("there are no approvals to deny"); | ||||
|             if (authStateProvider.CurrentUser is null) { | ||||
|                 navigationManager.NavigateTo("login"); | ||||
|                 return; | ||||
|             } | ||||
|             if (mrb is null) throw new Exception("MRB is null"); | ||||
|  | ||||
|             string? comments = ""; | ||||
|  | ||||
|             DialogParameters<Comments> parameters = new DialogParameters<Comments> { { x => x.comments, comments } }; | ||||
|             var dialog = dialogService.Show<Comments>($"Denial Comments", parameters); | ||||
|  | ||||
|             var result = await dialog.Result; | ||||
|  | ||||
|             comments = result.Data.ToString(); | ||||
|  | ||||
|             if (result.Canceled) throw new Exception("you must provide your denial comments"); | ||||
|  | ||||
|             IEnumerable<Approval> approvals = mrbApprovals.Where(a => a.UserID == authStateProvider.CurrentUser.UserID && | ||||
|                                                                       !(a.CompletedDate < DateTime.MaxValue) && | ||||
|                                                                       a.Step == mrb.StageNo); | ||||
|  | ||||
|             foreach (Approval approval in approvals) { | ||||
|                 approval.CompletedDate = DateTime.Now; | ||||
|                 approval.Comments = comments is null ? "" : comments; | ||||
|                 approval.ItemStatus = -1; | ||||
|                 await approvalService.Deny(approval); | ||||
|             } | ||||
|  | ||||
|             if (mrb.StageNo < 2) { | ||||
|                 mrb.StageNo = 0; | ||||
|                 mrb.SubmittedDate = DateTime.MinValue; | ||||
|             } else { | ||||
|                 IEnumerable<Approval> remainingApprovalsInKind = approvals.Where(a => a.Step == mrb.StageNo && | ||||
|                                                                              a.UserID != authStateProvider.CurrentUser.UserID && | ||||
|                                                                              !(a.CompletedDate < DateTime.MaxValue)); | ||||
|  | ||||
|                 if (remainingApprovalsInKind is null || remainingApprovalsInKind.Count() <= 0) { | ||||
|                     mrb.CloseDate = DateTime.Now; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             await mrbService.UpdateMRB(mrb); | ||||
|  | ||||
|             mrbApprovals = await approvalService.GetApprovalsForIssueId(mrb.MRBNumber, true); | ||||
|             nonTaskApprovals = mrbApprovals.Where(a => a.Step < 3).ToList(); | ||||
|  | ||||
|             string body = $"Your MRB ({mrb.MRBNumber}) has been denied."; | ||||
|  | ||||
|             MRBNotification notification = new() { | ||||
|                     MRB = mrb, | ||||
|                     Message = body | ||||
|                 }; | ||||
|  | ||||
|             await mrbService.NotifyOriginator(notification); | ||||
|  | ||||
|             denialInProcess = false; | ||||
|  | ||||
|             StateHasChanged(); | ||||
|  | ||||
|             snackbar.Add("Successfully approved", Severity.Success); | ||||
|         } catch (Exception ex) { | ||||
|             denialInProcess = false; | ||||
|  | ||||
|             snackbar.Add($"Unable to approve, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async void GenerateActionTasks() { | ||||
|         try { | ||||
|             if (mrb is null) throw new Exception("MRB cannot be null"); | ||||
|  | ||||
|             foreach (MRBAction action in mrbActions) { | ||||
|                 action.AssignedDate = DateTime.Now; | ||||
|                 await mrbService.UpdateMRBAction(action); | ||||
|  | ||||
|                 await mrbService.GenerateActionTasks(mrb, action); | ||||
|             } | ||||
|  | ||||
|             await mrbService.NotifyNewApprovals(mrb); | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add($"Unable to generate action tasks, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async void CompleteAction(MRBAction action) { | ||||
|         try { | ||||
|             if (action is null) throw new Exception("MRB action cannot be null"); | ||||
|             if (authStateProvider.CurrentUser is null) | ||||
|                 throw new Exception("you must be logged in to complete this action"); | ||||
|             if (mrb is null) throw new Exception("MRB cannot be null"); | ||||
|  | ||||
|             action.CompletedDate = DateTime.Now; | ||||
|             action.CompletedByUserID = authStateProvider.CurrentUser.UserID; | ||||
|             action.CompletedByUser = authStateProvider.CurrentUser; | ||||
|  | ||||
|             await mrbService.UpdateMRBAction(action); | ||||
|  | ||||
|             mrbActions = await mrbService.GetMRBActionsForMRB(action.MRBNumber, true); | ||||
|  | ||||
|             string role = ""; | ||||
|             foreach (Approval approval in taskApprovals) { | ||||
|                 bool approved = false; | ||||
|                 if (approval.UserID == action.CompletedByUserID && approval.ItemStatus == 0 && approval.TaskID == action.ActionID) { | ||||
|                     approved = true; | ||||
|                     role = approval.SubRoleCategoryItem; | ||||
|                     await approvalService.Approve(approval); | ||||
|                 } | ||||
|  | ||||
|                 if (!approved && approval.SubRoleCategoryItem.Equals(role)) | ||||
|                     await approvalService.Approve(approval); | ||||
|             } | ||||
|  | ||||
|             mrbApprovals = await approvalService.GetApprovalsForIssueId(mrb.MRBNumber, true); | ||||
|             taskApprovals = mrbApprovals.Where(a => a.Step >= 3).ToList(); | ||||
|  | ||||
|             int outStandingTaskCount = taskApprovals.Where(a => a.CompletedDate >= DateTime.MaxValue).Count(); | ||||
|  | ||||
|             if (outStandingTaskCount == 0) { | ||||
|                 mrb.StageNo++; | ||||
|                 mrb.CloseDate = DateTime.Now; | ||||
|                 await mrbService.UpdateMRB(mrb); | ||||
|  | ||||
|                 string body = $"Your MRB ({mrb.MRBNumber}) is complete."; | ||||
|  | ||||
|                 MRBNotification notification = new() { | ||||
|                         MRB = mrb, | ||||
|                         Message = body | ||||
|                     }; | ||||
|  | ||||
|                 await mrbService.NotifyOriginator(notification); | ||||
|             } | ||||
|  | ||||
|             StateHasChanged(); | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add($"Unable to mark action complete, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private bool mrbIsReadyToSubmit() { | ||||
|         return mrb is not null && !(mrb.SubmittedDate > DateTime.MinValue) && mrb.MRBNumber > 0 && mrb.OriginatorID > 0 && !mrb.OriginatorName.Equals("") && | ||||
|             !mrb.Title.Equals("") && !mrb.IssueDescription.Equals("") && !mrb.Department.Equals("") && !mrb.Process.Equals(""); | ||||
|     } | ||||
|  | ||||
|     private bool currentUserIsApprover() { | ||||
|         if (mrbApprovals is null || authStateProvider is null) return false; | ||||
|         if (authStateProvider.CurrentUser is null) return false; | ||||
|  | ||||
|         IEnumerable<Approval> approvalsForCurrentUser = mrbApprovals.Where(a => mrb is not null && | ||||
|                                                                                 mrb.StageNo < 3 && | ||||
|                                                                                 a.UserID == authStateProvider.CurrentUser.UserID && | ||||
|                                                                                 a.ItemStatus == 0); | ||||
|  | ||||
|         if (approvalsForCurrentUser is not null && approvalsForCurrentUser.Count() > 0) return true; | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     private async void CreateNewAction() { | ||||
|         try { | ||||
|             MRBAction mrbAction = new() { | ||||
|                     Action = "", | ||||
|                     Customer = "", | ||||
|                     LotNumber = "", | ||||
|                     PartNumber = "", | ||||
|                     MRBNumber = mrbNumberInt, | ||||
|                     Quantity = 0 | ||||
|                 }; | ||||
|  | ||||
|             DialogParameters<MRBActionForm> parameters = new DialogParameters<MRBActionForm> { { x => x.mrbAction, mrbAction } }; | ||||
|             var dialog = dialogService.Show<MRBActionForm>($"New MRB Action", parameters); | ||||
|  | ||||
|             var result = await dialog.Result; | ||||
|  | ||||
|             if (!result.Canceled) { | ||||
|                 if (mrbNumberInt > 0) { | ||||
|                     mrbActions = await mrbService.GetMRBActionsForMRB(mrbNumberInt, true); | ||||
|                 } else { | ||||
|                     List<MRBAction> actionList = mrbActions.ToList(); | ||||
|                     actionList.Add(mrbAction); | ||||
|                     mrbActions = actionList; | ||||
|                 } | ||||
|                 StateHasChanged(); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async void EditAction(MRBAction mrbAction) { | ||||
|         try { | ||||
|             if (mrbAction is null) | ||||
|                 throw new ArgumentNullException("Action cannot be null"); | ||||
|  | ||||
|             var parameters = new DialogParameters<MRBActionForm> { { x => x.mrbAction, mrbAction } }; | ||||
|             var dialog = dialogService.Show<MRBActionForm>($"MRB Action {mrbAction.ActionID}", parameters); | ||||
|  | ||||
|             var result = await dialog.Result; | ||||
|  | ||||
|             if (!result.Canceled) { | ||||
|                 if (mrbNumberInt > 0) { | ||||
|                     mrbActions = await mrbService.GetMRBActionsForMRB(mrbNumberInt, true); | ||||
|                 } else { | ||||
|                     List<MRBAction> actionList = mrbActions.ToList(); | ||||
|                     actionList.Add(mrbAction); | ||||
|                     mrbActions = actionList; | ||||
|                 } | ||||
|                 StateHasChanged(); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async void DeleteAction(MRBAction mrbAction) { | ||||
|         deleteActionInProcess = true; | ||||
|         try { | ||||
|             if (mrbAction is null) | ||||
|                 throw new ArgumentNullException("Action cannot be null"); | ||||
|  | ||||
|             await mrbService.DeleteMRBAction(mrbAction); | ||||
|  | ||||
|             List<MRBAction> mrbActionList = mrbActions.ToList(); | ||||
|             mrbActionList.RemoveAll(x => x.ActionID == mrbAction.ActionID); | ||||
|             mrbActions = mrbActionList; | ||||
|  | ||||
|             snackbar.Add("Action successfully deleted", Severity.Success); | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
|         } | ||||
|         deleteActionInProcess = false; | ||||
|         StateHasChanged(); | ||||
|     } | ||||
|  | ||||
|     private bool FilterFuncForMRBActionTable(MRBAction action) => MRBActionFilterFunc(action, actionSearchString); | ||||
|  | ||||
|     private bool MRBActionFilterFunc(MRBAction action, string searchString) { | ||||
|         if (string.IsNullOrWhiteSpace(searchString)) | ||||
|             return true; | ||||
|  | ||||
|         string search = searchString.ToLower(); | ||||
|         if (action.Customer.ToLower().Contains(search)) | ||||
|             return true; | ||||
|         if (action.Action.ToLower().Contains(search)) | ||||
|             return true; | ||||
|         if (action.PartNumber.ToLower().Contains(search)) | ||||
|             return true; | ||||
|         if (action.LotNumber.ToLower().Contains(search)) | ||||
|             return true; | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     private async Task AddAttachments(InputFileChangeEventArgs args) { | ||||
|         List<IBrowserFile> attachments = new() { args.File }; | ||||
|  | ||||
|         if (authStateProvider.CurrentUser is not null) { | ||||
|             await mrbService.UploadAttachments(attachments, mrbNumberInt); | ||||
|  | ||||
|             mrbAttachments = await mrbService.GetAllAttachmentsForMRB(mrbNumberInt, true); | ||||
|             StateHasChanged(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async void DeleteAttachment(MRBAttachment mrbAttachment) { | ||||
|         deleteAttachmentInProcess = true; | ||||
|         try { | ||||
|             if (mrbAttachment is null) | ||||
|                 throw new ArgumentNullException("Attachment cannot be null"); | ||||
|  | ||||
|             await mrbService.DeleteAttachment(mrbAttachment); | ||||
|  | ||||
|             List<MRBAttachment> mrbAttachmentList = mrbAttachments.ToList(); | ||||
|             mrbAttachmentList.RemoveAll(x => x.AttachmentID == mrbAttachment.AttachmentID); | ||||
|             mrbAttachments = mrbAttachmentList; | ||||
|  | ||||
|             snackbar.Add("Attachment successfully deleted", Severity.Success); | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
|         } | ||||
|         deleteAttachmentInProcess = false; | ||||
|         StateHasChanged(); | ||||
|     } | ||||
|  | ||||
|     private bool FilterFuncForMRBAttachmentTable(MRBAttachment attachment) => MRBAttachmentFilterFunc(attachment, attachmentSearchString); | ||||
|  | ||||
|     private bool MRBAttachmentFilterFunc(MRBAttachment attachment, string searchString) { | ||||
|         if (string.IsNullOrWhiteSpace(searchString)) | ||||
|             return true; | ||||
|  | ||||
|         string search = searchString.ToLower(); | ||||
|         if (attachment.FileName.ToLower().Contains(search)) | ||||
|             return true; | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										56
									
								
								MesaFabApproval.Client/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								MesaFabApproval.Client/Program.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| 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; | ||||
| using Blazored.SessionStorage; | ||||
|  | ||||
| WebAssemblyHostBuilder builder = WebAssemblyHostBuilder.CreateDefault(args); | ||||
|  | ||||
| string _apiBaseUrl = builder.Configuration["FabApprovalApiBaseUrl"] ?? | ||||
|     throw new NullReferenceException("FabApprovalApiBaseUrl not found in config"); | ||||
|  | ||||
| builder.Services.AddBlazoredSessionStorage(); | ||||
|  | ||||
| builder.Services.AddTransient<ApiHttpClientHandler>(); | ||||
|  | ||||
| builder.Services | ||||
|     .AddHttpClient("API", client => { | ||||
|         client.BaseAddress = new Uri(_apiBaseUrl); | ||||
|         client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*")); | ||||
|     }) | ||||
|     .AddHttpMessageHandler<ApiHttpClientHandler>(); | ||||
|  | ||||
| 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 = 5000; | ||||
|     config.SnackbarConfiguration.HideTransitionDuration = 500; | ||||
|     config.SnackbarConfiguration.ShowTransitionDuration = 500; | ||||
| }); | ||||
|  | ||||
| builder.Services.AddScoped<IAuthenticationService, AuthenticationService>(); | ||||
| builder.Services.AddScoped<IUserService, UserService>(); | ||||
| 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(); | ||||
| @ -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> | ||||
							
								
								
									
										303
									
								
								MesaFabApproval.Client/Services/ApprovalService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								MesaFabApproval.Client/Services/ApprovalService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,303 @@ | ||||
| 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}"); | ||||
|         } | ||||
|  | ||||
|         await GetApprovalsForIssueId(approval.IssueID, true); | ||||
|         await GetApprovalsForUserId(approval.UserID, true); | ||||
|     } | ||||
|  | ||||
|     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(15)); | ||||
|             } else { | ||||
|                 throw new Exception($"Unable to get group members, because {responseMessage.ReasonPhrase}"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return members; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										187
									
								
								MesaFabApproval.Client/Services/AuthenticationService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								MesaFabApproval.Client/Services/AuthenticationService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,187 @@ | ||||
| using System.IdentityModel.Tokens.Jwt; | ||||
| using System.Security.Claims; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
|  | ||||
| using Blazored.SessionStorage; | ||||
|  | ||||
| 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> 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 ISessionStorageService _sessionStorageService; | ||||
|     private readonly IMemoryCache _cache; | ||||
|     private readonly IHttpClientFactory _httpClientFactory; | ||||
|  | ||||
|     public AuthenticationService(ISessionStorageService sessionStorageService, | ||||
|                                  IMemoryCache cache, | ||||
|                                  IHttpClientFactory httpClientFactory) { | ||||
|         _sessionStorageService = sessionStorageService ?? | ||||
|             throw new ArgumentNullException("ISessionStorageService 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> FetchAuthState() { | ||||
|         string? jwt = _cache.Get<string>("MesaFabApprovalJwt"); | ||||
|  | ||||
|         if (jwt is null) jwt = await _sessionStorageService.GetItemAsync<string>("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 _sessionStorageService.RemoveItemAsync("MesaFabApprovalJwt"); | ||||
|         _cache.Remove("MesaFabApprovalRefreshToken"); | ||||
|         await _sessionStorageService.RemoveItemAsync("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 _sessionStorageService.SetItemAsync<string>("MesaFabApprovalJwt", jwt); | ||||
|         _cache.Set<string>("MesaFabApprovalRefreshToken", refreshToken); | ||||
|         await _sessionStorageService.SetItemAsync<string>("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 _sessionStorageService.SetItemAsync<string>("MesaFabApprovalUserId", loginId); | ||||
|     } | ||||
|  | ||||
|     public async Task SetCurrentUser(User user) { | ||||
|         if (user is null) throw new ArgumentNullException("User cannot be null"); | ||||
|  | ||||
|         _cache.Set<User>("MesaFabApprovalCurrentUser", user); | ||||
|         await _sessionStorageService.SetItemAsync<User>("MesaFabApprovalCurrentUser", user); | ||||
|     } | ||||
|  | ||||
|     public async Task ClearCurrentUser() { | ||||
|         _cache.Remove("MesaFabApprovalCurrentUser"); | ||||
|         await _sessionStorageService.RemoveItemAsync("MesaFabApprovalCurrentUser"); | ||||
|         _cache.Remove("MesaFabApprovalUserId"); | ||||
|         await _sessionStorageService.RemoveItemAsync("MesaFabApprovalUserId"); | ||||
|     } | ||||
|  | ||||
|     public async Task<User> GetCurrentUser() { | ||||
|         User? currentUser = null; | ||||
|  | ||||
|         currentUser = _cache.Get<User>("MesaFabApprovalCurrentUser") ?? | ||||
|             await _sessionStorageService.GetItemAsync<User>("MesaFabApprovalCurrentUser"); | ||||
|  | ||||
|         return currentUser; | ||||
|     } | ||||
|  | ||||
|     public async Task<AuthTokens> GetAuthTokens() { | ||||
|         AuthTokens? authTokens = null; | ||||
|  | ||||
|         string? jwt = _cache.Get<string>("MesaFabApprovalJwt"); | ||||
|         if (jwt is null) jwt = await _sessionStorageService.GetItemAsync<string>("MesaFabApprovalJwt"); | ||||
|  | ||||
|         string? refreshToken = _cache.Get<string>("MesaFabApprovalRefreshToken"); | ||||
|         if (refreshToken is null) refreshToken = await _sessionStorageService.GetItemAsync<string>("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 _sessionStorageService.GetItemAsync<string>("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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										537
									
								
								MesaFabApproval.Client/Services/MRBService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										537
									
								
								MesaFabApproval.Client/Services/MRBService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,537 @@ | ||||
| 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 IMRBService { | ||||
|     Task<IEnumerable<MRB>> GetAllMRBs(); | ||||
|     Task<MRB> GetMRBById(int id); | ||||
|     Task<MRB> GetMRBByTitle(string title, bool bypassCache); | ||||
|     Task CreateNewMRB(MRB mrb); | ||||
|     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<IEnumerable<MRBAttachment>> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache); | ||||
|     Task DeleteAttachment(MRBAttachment attachment); | ||||
|     Task NotifyNewApprovals(MRB mrb); | ||||
|     Task NotifyApprovers(MRBNotification notification); | ||||
|     Task NotifyOriginator(MRBNotification notification); | ||||
| } | ||||
|  | ||||
| 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<IEnumerable<MRB>> GetAllMRBs() { | ||||
|         try { | ||||
|             IEnumerable<MRB>? allMRBs = _cache.Get<IEnumerable<MRB>>("allMrbs"); | ||||
|  | ||||
|             if (allMRBs is null) { | ||||
|                 HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|                 HttpRequestMessage requestMessage = new(HttpMethod.Get, "mrb/all"); | ||||
|  | ||||
|                 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<MRB> GetMRBById(int id) { | ||||
|         if (id <= 0) throw new ArgumentException($"Invalid MRB number: {id}"); | ||||
|  | ||||
|         MRB? mrb = _cache.Get<MRB>($"mrb{id}"); | ||||
|  | ||||
|         if (mrb is null) 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}"); | ||||
|  | ||||
|             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 emtpy"); | ||||
|  | ||||
|         MRB? mrb = null; | ||||
|         if (!bypassCache) mrb = _cache.Get<MRB>($"mrb{title}"); | ||||
|  | ||||
|         if (mrb is null) 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 { | ||||
|                 StreamContent fileContent = new StreamContent(file.OpenReadStream()); | ||||
|  | ||||
|                 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}"); | ||||
|  | ||||
|         await GetAllAttachmentsForMRB(mrbNumber, 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 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}"); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,88 @@ | ||||
| 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))); | ||||
|     } | ||||
|  | ||||
|     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 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) { | ||||
|             Console.WriteLine($"Unable to get authentication state, because {ex.Message}"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										207
									
								
								MesaFabApproval.Client/Services/UserService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								MesaFabApproval.Client/Services/UserService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,207 @@ | ||||
| using System.Security.Claims; | ||||
| using System.Text.Json; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
|  | ||||
| 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) | ||||
|             activeUsers = new List<User>(); | ||||
|  | ||||
|         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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										11
									
								
								MesaFabApproval.Client/Util/DateTimeUtilities.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								MesaFabApproval.Client/Util/DateTimeUtilities.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| namespace MesaFabApproval.Client.Util; | ||||
|  | ||||
| public class DateTimeUtilities { | ||||
|     public static string GetDateAsStringMinDefault(DateTime dt) { | ||||
|         return dt > DateTime.MinValue ? dt.ToString("yyyy-MM-dd HH:mm") : ""; | ||||
|     } | ||||
|  | ||||
|     public static string GetDateAsStringMaxDefault(DateTime dt) { | ||||
|         return dt < DateTime.MaxValue ? dt.ToString("yyyy-MM-dd HH:mm") : ""; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										107
									
								
								MesaFabApproval.Client/Utilities/ApiHttpClientHandler.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								MesaFabApproval.Client/Utilities/ApiHttpClientHandler.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | ||||
| using System.Net; | ||||
| using System.Text.Json; | ||||
| using System.Text; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
|  | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
| using System.Net.Http.Headers; | ||||
| using MesaFabApproval.Client.Services; | ||||
| using Microsoft.AspNetCore.Components; | ||||
|  | ||||
| namespace MesaFabApproval.Client.Utilities; | ||||
|  | ||||
| public class ApiHttpClientHandler : DelegatingHandler { | ||||
|     private readonly IMemoryCache _cache; | ||||
|     private readonly IAuthenticationService _authService; | ||||
|     private readonly MesaFabApprovalAuthStateProvider _authStateProvider; | ||||
|     private readonly NavigationManager _navigationManager; | ||||
|  | ||||
|     private readonly string _apiBaseUrl; | ||||
|  | ||||
|     public ApiHttpClientHandler(IMemoryCache cache, | ||||
|                                 IConfiguration config, | ||||
|                                 IAuthenticationService authService, | ||||
|                                 MesaFabApprovalAuthStateProvider authStateProvider, | ||||
|                                 NavigationManager navigationManager) { | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _apiBaseUrl = config["FabApprovalApiBaseUrl"] ?? | ||||
|             throw new NullReferenceException("FabApprovalApiBaseUrl not found in config"); | ||||
|         _authService = authService ?? | ||||
|             throw new ArgumentNullException("IAuthenticationService not injected"); | ||||
|         _authStateProvider = authStateProvider ?? | ||||
|             throw new ArgumentNullException("MesaFabApprovalAuthStateProvider not injected"); | ||||
|         _navigationManager = navigationManager ?? | ||||
|             throw new ArgumentNullException("NavigationManager not injected"); | ||||
|     } | ||||
|  | ||||
|     protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessage, | ||||
|                                                                  CancellationToken cancellationToken) { | ||||
|         AuthTokens? authTokens = await _authService.GetAuthTokens(); | ||||
|  | ||||
|         if (authTokens is not null) requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authTokens.JwtToken); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await base.SendAsync(requestMessage, cancellationToken); | ||||
|  | ||||
|         if (responseMessage.StatusCode.Equals(HttpStatusCode.Unauthorized)) { | ||||
|             string? loginId = await _authService.GetLoginId(); | ||||
|  | ||||
|             if (!string.IsNullOrWhiteSpace(loginId) && authTokens is not null) { | ||||
|                 AuthAttempt authAttempt = new() { | ||||
|                     LoginID = loginId, | ||||
|                     AuthTokens = authTokens | ||||
|                 }; | ||||
|  | ||||
|                 HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "auth/refresh"); | ||||
|  | ||||
|                 request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authTokens.JwtToken); | ||||
|  | ||||
|                 request.Content = new StringContent(JsonSerializer.Serialize(authAttempt), | ||||
|                                                     Encoding.UTF8, | ||||
|                                                     "application/json"); | ||||
|  | ||||
|                 HttpClient httpClient = new HttpClient() { | ||||
|                     BaseAddress = new Uri(_apiBaseUrl) | ||||
|                 }; | ||||
|  | ||||
|                 httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*")); | ||||
|                  | ||||
|                 HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request, cancellationToken); | ||||
|  | ||||
|                 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"); | ||||
|  | ||||
|                     if (loginResult.AuthTokens is not null) { | ||||
|                         requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", loginResult.AuthTokens.JwtToken); | ||||
|                         await _authService.SetTokens(loginResult.AuthTokens.JwtToken, loginResult.AuthTokens.RefreshToken); | ||||
|                     } | ||||
|  | ||||
|                     if (loginResult.User is not null) | ||||
|                         await _authService.SetCurrentUser(loginResult.User); | ||||
|                 } else { | ||||
|                     await _authStateProvider.Logout(); | ||||
|  | ||||
|                     string? redirectUrl = _cache.Get<string>("redirectUrl"); | ||||
|                     if (!string.IsNullOrWhiteSpace(redirectUrl)) { | ||||
|                         _navigationManager.NavigateTo($"login/{redirectUrl}"); | ||||
|                     } else { | ||||
|                         _navigationManager.NavigateTo("login"); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return await base.SendAsync(requestMessage, cancellationToken); | ||||
|         } | ||||
|  | ||||
|         return responseMessage; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										22
									
								
								MesaFabApproval.Client/_Imports.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								MesaFabApproval.Client/_Imports.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| @using System.IdentityModel.Tokens.Jwt | ||||
| @using System.Net.Http | ||||
| @using System.Net.Http.Json | ||||
| @using Microsoft.AspNetCore.Components.Authorization | ||||
| @using Microsoft.AspNetCore.Components.Forms | ||||
| @using Microsoft.AspNetCore.Components.Routing | ||||
| @using Microsoft.AspNetCore.Components.Web | ||||
| @using Microsoft.AspNetCore.Components.Web.Virtualization | ||||
| @using Microsoft.AspNetCore.Components.WebAssembly.Http | ||||
| @using Microsoft.JSInterop | ||||
| @using MesaFabApproval.Client | ||||
| @using MesaFabApproval.Client.Layout | ||||
| @using MesaFabApproval.Client.Pages.Components | ||||
| @using MesaFabApproval.Client.Services | ||||
| @using MesaFabApproval.Client.Util | ||||
| @using MesaFabApproval.Shared.Models | ||||
| @using MudBlazor | ||||
| @using Microsoft.AspNetCore.Authorization | ||||
| @using System.Security.Claims | ||||
| @using Microsoft.Extensions.Caching.Memory | ||||
| @using Microsoft.AspNetCore.WebUtilities | ||||
| @attribute [Authorize] | ||||
							
								
								
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/android-chrome-192x192.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/android-chrome-192x192.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 6.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/android-chrome-512x512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/android-chrome-512x512.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 24 KiB | 
							
								
								
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/apple-touch-icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/apple-touch-icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 6.3 KiB | 
							
								
								
									
										4
									
								
								MesaFabApproval.Client/wwwroot/appsettings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								MesaFabApproval.Client/wwwroot/appsettings.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| { | ||||
|   "OldFabApprovalUrl": "https://mesaapproval-test.mes.infineon.com", | ||||
|   "FabApprovalApiBaseUrl": "https://mesaapproval-test.mes.infineon.com:7114" | ||||
| } | ||||
							
								
								
									
										103
									
								
								MesaFabApproval.Client/wwwroot/css/app.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								MesaFabApproval.Client/wwwroot/css/app.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | ||||
| html, body { | ||||
|     font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; | ||||
| } | ||||
|  | ||||
| h1:focus { | ||||
|     outline: none; | ||||
| } | ||||
|  | ||||
| a, .btn-link { | ||||
|     color: #0071c1; | ||||
| } | ||||
|  | ||||
| .btn-primary { | ||||
|     color: #fff; | ||||
|     background-color: #1b6ec2; | ||||
|     border-color: #1861ac; | ||||
| } | ||||
|  | ||||
| .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { | ||||
|   box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; | ||||
| } | ||||
|  | ||||
| .content { | ||||
|     padding-top: 1.1rem; | ||||
| } | ||||
|  | ||||
| .valid.modified:not([type=checkbox]) { | ||||
|     outline: 1px solid #26b050; | ||||
| } | ||||
|  | ||||
| .invalid { | ||||
|     outline: 1px solid red; | ||||
| } | ||||
|  | ||||
| .validation-message { | ||||
|     color: red; | ||||
| } | ||||
|  | ||||
| #blazor-error-ui { | ||||
|     background: lightyellow; | ||||
|     bottom: 0; | ||||
|     box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); | ||||
|     display: none; | ||||
|     left: 0; | ||||
|     padding: 0.6rem 1.25rem 0.7rem 1.25rem; | ||||
|     position: fixed; | ||||
|     width: 100%; | ||||
|     z-index: 1000; | ||||
| } | ||||
|  | ||||
|     #blazor-error-ui .dismiss { | ||||
|         cursor: pointer; | ||||
|         position: absolute; | ||||
|         right: 0.75rem; | ||||
|         top: 0.5rem; | ||||
|     } | ||||
|  | ||||
| .blazor-error-boundary { | ||||
|     background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; | ||||
|     padding: 1rem 1rem 1rem 3.7rem; | ||||
|     color: white; | ||||
| } | ||||
|  | ||||
|     .blazor-error-boundary::after { | ||||
|         content: "An error has occurred." | ||||
|     } | ||||
|  | ||||
| .loading-progress { | ||||
|     position: relative; | ||||
|     display: block; | ||||
|     width: 8rem; | ||||
|     height: 8rem; | ||||
|     margin: 20vh auto 1rem auto; | ||||
| } | ||||
|  | ||||
|     .loading-progress circle { | ||||
|         fill: none; | ||||
|         stroke: #e0e0e0; | ||||
|         stroke-width: 0.6rem; | ||||
|         transform-origin: 50% 50%; | ||||
|         transform: rotate(-90deg); | ||||
|     } | ||||
|  | ||||
|         .loading-progress circle:last-child { | ||||
|             stroke: #1b6ec2; | ||||
|             stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; | ||||
|             transition: stroke-dasharray 0.05s ease-in-out; | ||||
|         } | ||||
|  | ||||
| .loading-progress-text { | ||||
|     position: absolute; | ||||
|     text-align: center; | ||||
|     font-weight: bold; | ||||
|     inset: calc(20vh + 3.25rem) 0 auto 0.2rem; | ||||
| } | ||||
|  | ||||
|     .loading-progress-text:after { | ||||
|         content: var(--blazor-load-percentage-text, "Loading"); | ||||
|     } | ||||
|  | ||||
| code { | ||||
|     color: #c02d76; | ||||
| } | ||||
							
								
								
									
										7
									
								
								MesaFabApproval.Client/wwwroot/css/bootstrap/bootstrap.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								MesaFabApproval.Client/wwwroot/css/bootstrap/bootstrap.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/favicon-16x16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/favicon-16x16.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 446 B | 
							
								
								
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/favicon-32x32.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/favicon-32x32.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 865 B | 
							
								
								
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								MesaFabApproval.Client/wwwroot/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 15 KiB | 
							
								
								
									
										49
									
								
								MesaFabApproval.Client/wwwroot/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								MesaFabApproval.Client/wwwroot/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|  | ||||
| <head> | ||||
|     <meta charset="utf-8" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>Mesa Fab Approval</title> | ||||
|     <base href="/" /> | ||||
|     <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" /> | ||||
|     <link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" /> | ||||
|     <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" /> | ||||
|     <link rel="stylesheet" href="css/app.css" /> | ||||
|     <link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png"> | ||||
|     <link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png"> | ||||
|     <link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png"> | ||||
|     <link rel="manifest" href="site.webmanifest"> | ||||
|     <link href="MesaFabApproval.Client.styles.css" rel="stylesheet" /> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|     <div id="app"> | ||||
|         <svg class="loading-progress"> | ||||
|             <circle r="40%" cx="50%" cy="50%" /> | ||||
|             <circle r="40%" cx="50%" cy="50%" /> | ||||
|         </svg> | ||||
|         <div class="loading-progress-text"></div> | ||||
|     </div> | ||||
|  | ||||
|     <div id="blazor-error-ui"> | ||||
|         An unhandled error has occurred. | ||||
|         <a href="" class="reload">Reload</a> | ||||
|         <a class="dismiss">🗙</a> | ||||
|     </div> | ||||
|     <script src="_content/MudBlazor/MudBlazor.min.js"></script> | ||||
|     <script src="_framework/blazor.webassembly.js" autostart="false"></script> | ||||
|     <script> | ||||
|         if (window.location.hostname.includes("localhost")) { | ||||
|             Blazor.start({ | ||||
|                 webAssembly: { | ||||
|                     environment: "Development" | ||||
|                 } | ||||
|             }); | ||||
|         } else { | ||||
|             Blazor.start(); | ||||
|         } | ||||
|     </script> | ||||
| </body> | ||||
|  | ||||
| </html> | ||||
							
								
								
									
										1
									
								
								MesaFabApproval.Client/wwwroot/site.webmanifest
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								MesaFabApproval.Client/wwwroot/site.webmanifest
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| {"name":"Mesa Fab Approval","short_name":"MesaFabApproval","icons":[{"src":"android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} | ||||
							
								
								
									
										16
									
								
								MesaFabApproval.Shared/MesaFabApproval.Shared.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								MesaFabApproval.Shared/MesaFabApproval.Shared.csproj
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <TargetFramework>net8.0</TargetFramework> | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
|     <Nullable>enable</Nullable> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.6" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
| </Project> | ||||
							
								
								
									
										20
									
								
								MesaFabApproval.Shared/Models/Approval.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								MesaFabApproval.Shared/Models/Approval.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class Approval { | ||||
|     public int ApprovalID { get; set; } | ||||
|     public required int IssueID { get; set; } | ||||
|     public required string RoleName { get; set; } | ||||
|     public required string SubRole { get; set; } | ||||
|     public required int UserID { get; set; } | ||||
|     public User? User { get; set; } | ||||
|     public required int SubRoleID { get; set; } | ||||
|     public int ItemStatus { get; set; } = 0; | ||||
|     public string StatusMessage = "Assigned"; | ||||
|     public DateTime NotifyDate { get; set; } = DateTime.MinValue; | ||||
|     public required DateTime AssignedDate {  get; set; } | ||||
|     public DateTime CompletedDate { get; set; } = DateTime.MaxValue; | ||||
|     public string Comments { get; set; } = ""; | ||||
|     public int Step { get; set; } = 1; | ||||
|     public string SubRoleCategoryItem { get; set; } = ""; | ||||
|     public int TaskID { get; set; } | ||||
| } | ||||
							
								
								
									
										7
									
								
								MesaFabApproval.Shared/Models/AuthAttempt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								MesaFabApproval.Shared/Models/AuthAttempt.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class AuthAttempt { | ||||
|     public required string LoginID { get; set; } | ||||
|     public string Password { get; set; } = ""; | ||||
|     public AuthTokens? AuthTokens { get; set; } | ||||
| } | ||||
							
								
								
									
										6
									
								
								MesaFabApproval.Shared/Models/AuthTokens.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								MesaFabApproval.Shared/Models/AuthTokens.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class AuthTokens { | ||||
|     public required string JwtToken { get; set; } | ||||
|     public required string RefreshToken { get; set; } | ||||
| } | ||||
							
								
								
									
										7
									
								
								MesaFabApproval.Shared/Models/LoginResult.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								MesaFabApproval.Shared/Models/LoginResult.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class LoginResult { | ||||
|     public required bool IsAuthenticated { get; set; } | ||||
|     public required User User { get; set; } | ||||
|     public required AuthTokens AuthTokens { get; set; } | ||||
| } | ||||
							
								
								
									
										42
									
								
								MesaFabApproval.Shared/Models/MRB.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								MesaFabApproval.Shared/Models/MRB.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| using System.Collections.Immutable; | ||||
|  | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class MRB { | ||||
|     public static string[] Stages { get; } = { | ||||
|         "Draft", | ||||
|         "QA Pre Approval", | ||||
|         "Pending Approval", | ||||
|         "Approved", | ||||
|         "Complete" | ||||
|     }; | ||||
|  | ||||
|     public int MRBNumber { get; set; } | ||||
|     public int OriginatorID { get; set; } | ||||
|     public string OriginatorName { get; set; } = ""; | ||||
|     public string Title { get; set; } = ""; | ||||
|     public DateTime SubmittedDate { get; set; } = DateTime.MinValue; | ||||
|     public DateTime CloseDate { get; set; } = DateTime.MaxValue; | ||||
|     public DateTime CancelDate { get; set; } = DateTime.MaxValue; | ||||
|     public DateTime ApprovalDate { get; set; } = DateTime.MaxValue; | ||||
|     public string IssueDescription { get; set; } = ""; | ||||
|     public int NumberOfLotsAffected { get; set; } | ||||
|     public string Val { get; set; } = ""; | ||||
|     public bool CustomerImpacted { get; set; } = false; | ||||
|     public string Department { get; set; } = ""; | ||||
|     public string Process { get; set; } = ""; | ||||
|     public int RMANo { get; set; } | ||||
|     public int PCRBNo { get; set; } | ||||
|     public bool SpecsImpacted { get; set; } = false; | ||||
|     public bool TrainingRequired { get; set; } = false; | ||||
|     public IEnumerable<MRBAttachment>? Attachments { get; set; } | ||||
|     public IEnumerable<MRBAction>? Actions { get; set; } | ||||
|     public IEnumerable<int>? ApproverUserIds { get; set; } | ||||
|     public required int StageNo { get; set; } | ||||
|     public required string Status { get; set; } | ||||
|  | ||||
|     public sealed class StageApprovalData { | ||||
|         public required string RoleName { get; set; } | ||||
|         public required string SubRoleName { get; set; } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								MesaFabApproval.Shared/Models/MRBAction.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								MesaFabApproval.Shared/Models/MRBAction.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class MRBAction { | ||||
|     public int ActionID { get; set; } | ||||
|     public required string Action {  get; set; } | ||||
|     public required string Customer {  get; set; } | ||||
|     public required int Quantity { get; set; } | ||||
|     public required string PartNumber { get; set; } | ||||
|     public required string LotNumber { get; set; } | ||||
|     public required int MRBNumber { get; set; } | ||||
|     public DateTime AssignedDate { get; set; } = DateTime.MinValue; | ||||
|     public DateTime CompletedDate { get; set; } = DateTime.MaxValue; | ||||
|     public int CompletedByUserID { get; set; } = 0; | ||||
|     public User? CompletedByUser { get; set; } | ||||
| } | ||||
							
								
								
									
										13
									
								
								MesaFabApproval.Shared/Models/MRBAttachment.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								MesaFabApproval.Shared/Models/MRBAttachment.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| using Microsoft.AspNetCore.Components.Forms; | ||||
|  | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class MRBAttachment { | ||||
|     public int AttachmentID { get; set; } | ||||
|     public required int MRBNumber { get; set; } | ||||
|     public required string FileName { get; set; } | ||||
|     public required int UserID { get; set; } | ||||
|     public required DateTime UploadDate { get; set; } | ||||
|     public string? Path { get; set; } | ||||
|     public IBrowserFile? File { get; set; } | ||||
| } | ||||
							
								
								
									
										6
									
								
								MesaFabApproval.Shared/Models/MRBNotification.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								MesaFabApproval.Shared/Models/MRBNotification.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class MRBNotification { | ||||
|     public required string Message { get; set; } | ||||
|     public required MRB MRB { get; set; } | ||||
| } | ||||
							
								
								
									
										8
									
								
								MesaFabApproval.Shared/Models/MonInMetricRequest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								MesaFabApproval.Shared/Models/MonInMetricRequest.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class MonInMetricRequest { | ||||
|     public required string resource { get; set; } | ||||
|     public required DateTime dateTime { get; set; } | ||||
|     public required string metricName { get; set; } | ||||
|     public required double metricValue { get; set; } | ||||
| } | ||||
							
								
								
									
										8
									
								
								MesaFabApproval.Shared/Models/RawMonInStatusRequest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								MesaFabApproval.Shared/Models/RawMonInStatusRequest.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class RawMonInStatusRequest { | ||||
|     public required string resource { get; set; } | ||||
|     public required DateTime dateTime { get; set; } | ||||
|     public required string statusName { get; set; } | ||||
|     public required string statusValue { get; set; } | ||||
| } | ||||
							
								
								
									
										11
									
								
								MesaFabApproval.Shared/Models/Role.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								MesaFabApproval.Shared/Models/Role.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class Role { | ||||
|     private string Value {  get; set; } | ||||
|  | ||||
|     private Role(string value) { this.Value = value; } | ||||
|  | ||||
|     public static Role MRB_APPROVER { get { return new Role("MRB Approver"); } } | ||||
|  | ||||
|     public override string ToString() => Value; | ||||
| } | ||||
							
								
								
									
										10
									
								
								MesaFabApproval.Shared/Models/StatusValue.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								MesaFabApproval.Shared/Models/StatusValue.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public enum StatusValue { | ||||
|     Up, | ||||
|     Ok, | ||||
|     Warning, | ||||
|     Critical, | ||||
|     Down, | ||||
|     Unknown | ||||
| } | ||||
							
								
								
									
										13
									
								
								MesaFabApproval.Shared/Models/SubRole.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								MesaFabApproval.Shared/Models/SubRole.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| using System.Collections.Concurrent; | ||||
| using System.Collections.Immutable; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class SubRole { | ||||
|     public int SubRoleID { get; set; } | ||||
|     public required string SubRoleName { get; set; } | ||||
|     public required int SubRoleCategoryID { get; set; } | ||||
|     public required string SubRoleCategoryItem { get; set; } | ||||
| } | ||||
							
								
								
									
										7
									
								
								MesaFabApproval.Shared/Models/UploadResult.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								MesaFabApproval.Shared/Models/UploadResult.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class UploadResult { | ||||
|     public required bool UploadSuccessful { get; set; } | ||||
|     public string? FileName { get; set; } | ||||
|     public string? Error { get; set; } | ||||
| } | ||||
							
								
								
									
										20
									
								
								MesaFabApproval.Shared/Models/User.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								MesaFabApproval.Shared/Models/User.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class User { | ||||
|     public required int UserID { get; set; } | ||||
|     public required string LoginID { get; set; } | ||||
|     public required string FirstName { get; set; } | ||||
|     public required string LastName { get; set; } | ||||
|     public required string Email { get; set; } | ||||
|     public bool IsAdmin { get; set; } = false; | ||||
|     public bool IsManager { get; set; } = false; | ||||
|     public bool IsActive { get; set; } = false; | ||||
|     public bool OOO { get; set; } = false; | ||||
|     public DateTime OOOStartDate { get; set; } | ||||
|     public DateTime OOOExpirationDate { get; set; } | ||||
|     public int DelegatedTo { get; set; } | ||||
|  | ||||
|     public string GetFullName() { | ||||
|         return $"{FirstName} {LastName}"; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										178
									
								
								MesaFabApproval.Shared/Services/MonInWorkerClient.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								MesaFabApproval.Shared/Services/MonInWorkerClient.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,178 @@ | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
|  | ||||
| using Microsoft.Extensions.Configuration; | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using static System.Net.Mime.MediaTypeNames; | ||||
|  | ||||
| namespace MesaFabApproval.Shared.Services; | ||||
|  | ||||
| public interface IMonInWorkerClient { | ||||
|     void PostAverage(string metricName, double metricValue); | ||||
|     void PostCount(string metricName, double metricValue); | ||||
|     void PostStatus(string statusName, StatusValue statusValue); | ||||
| } | ||||
|  | ||||
| public class MonInWorkerClient : IMonInWorkerClient { | ||||
|     private readonly IHttpClientFactory _httpClientFactory; | ||||
|     private readonly IConfiguration _config; | ||||
|     private readonly ILogger<MonInWorkerClient> _logger; | ||||
|  | ||||
|     private readonly string _baseUrl; | ||||
|     private readonly int _retryLimit = -1; | ||||
|     private readonly int _backoffInSeconds = -1; | ||||
|  | ||||
|     private readonly string _resource; | ||||
|  | ||||
|     public MonInWorkerClient(IHttpClientFactory httpClientFactory, | ||||
|                              IConfiguration config, | ||||
|                              ILogger<MonInWorkerClient> logger) { | ||||
|         _httpClientFactory = httpClientFactory; | ||||
|         if (_httpClientFactory is null) throw new ArgumentNullException("IHttpClientFactory not injected"); | ||||
|  | ||||
|         _config = config; | ||||
|         if (_config is null) throw new ArgumentNullException("IConfiguration not injected"); | ||||
|  | ||||
|         _logger = logger; | ||||
|         if (_logger is null) throw new ArgumentNullException("ILogger not injected"); | ||||
|  | ||||
|         _baseUrl = Environment.GetEnvironmentVariable("MonInWorkerUrl") ?? | ||||
|             throw new ArgumentNullException("MonInWorkerUrl environment variable not found"); | ||||
|  | ||||
|         string monInRetries = Environment.GetEnvironmentVariable("MonInRetries") ?? | ||||
|             throw new ArgumentNullException("MonInRetries environment variable not found"); | ||||
|         if (!Int32.TryParse(monInRetries, out _retryLimit)) | ||||
|             throw new ArgumentException("MonInReties environment variable not an integer"); | ||||
|  | ||||
|         string monInBackoffSeconds = Environment.GetEnvironmentVariable("MonInBackoffSeconds") ?? | ||||
|             throw new ArgumentNullException("MonInBackoffSeconds environment variable not found"); | ||||
|         if (!Int32.TryParse(monInBackoffSeconds, out _backoffInSeconds)) | ||||
|             throw new ArgumentException("MonInBackoffSeconds environment variable not an integer"); | ||||
|  | ||||
|         _resource = Environment.GetEnvironmentVariable("FabApprovalApiMonInResource") ?? | ||||
|             throw new ArgumentNullException("OIWizardMonInResource environment variable not found"); | ||||
|     } | ||||
|  | ||||
|     public async void PostStatus(string statusName, StatusValue statusValue) { | ||||
|         string url = _baseUrl + "status"; | ||||
|  | ||||
|         StringBuilder logBuilder = new(); | ||||
|         logBuilder.Append($"Attempting to send MonIn status request for resource {_resource} "); | ||||
|         logBuilder.Append($"with name {statusName} and value {statusValue.ToString()} to url {url}"); | ||||
|         _logger.LogInformation(logBuilder.ToString()); | ||||
|  | ||||
|         try { | ||||
|             bool success = false; | ||||
|             int retries = _retryLimit; | ||||
|             while (!success && retries > 0) { | ||||
|                 Task.Delay(TimeSpan.FromSeconds((_retryLimit - retries) * _backoffInSeconds)).Wait(); | ||||
|  | ||||
|                 HttpClient httpClient = _httpClientFactory.CreateClient(); | ||||
|  | ||||
|                 RawMonInStatusRequest statusRequest = new RawMonInStatusRequest() { | ||||
|                     resource = _resource, | ||||
|                     dateTime = DateTime.Now, | ||||
|                     statusName = statusName, | ||||
|                     statusValue = statusValue.ToString() | ||||
|                 }; | ||||
|  | ||||
|                 StringContent statusRequestJson = new StringContent(JsonSerializer.Serialize(statusRequest), | ||||
|                                                           Encoding.UTF8, | ||||
|                                                           Application.Json); | ||||
|  | ||||
|                 using HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(url, statusRequestJson); | ||||
|  | ||||
|                 success = httpResponseMessage.IsSuccessStatusCode; | ||||
|                 retries--; | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             StringBuilder errLogBuilder = new(); | ||||
|             errLogBuilder.Append($"An exception occurred when attempting to send MonIn status request for resource {_resource} "); | ||||
|             errLogBuilder.Append($"with name {statusName} and value {statusValue.ToString()} to url {url}. Exception: {ex.Message}"); | ||||
|             _logger.LogError(errLogBuilder.ToString()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async void PostCount(string metricName, double metricValue) { | ||||
|         string url = _baseUrl + "count"; | ||||
|  | ||||
|         StringBuilder logMsgBuilder = new(); | ||||
|         logMsgBuilder.Append($"Attempting to send MonIn count request for resource {_resource} "); | ||||
|         logMsgBuilder.Append($"with name {metricName} and value {metricValue} to url {url}"); | ||||
|         _logger.LogInformation(logMsgBuilder.ToString()); | ||||
|  | ||||
|         try { | ||||
|             bool success = false; | ||||
|             int retries = _retryLimit; | ||||
|             while (!success && retries > 0) { | ||||
|                 Task.Delay(TimeSpan.FromSeconds((_retryLimit - retries) * _backoffInSeconds)).Wait(); | ||||
|  | ||||
|                 HttpClient httpClient = _httpClientFactory.CreateClient(); | ||||
|  | ||||
|                 MonInMetricRequest metricRequest = new MonInMetricRequest() { | ||||
|                     resource = _resource, | ||||
|                     dateTime = DateTime.Now, | ||||
|                     metricName = metricName, | ||||
|                     metricValue = metricValue | ||||
|                 }; | ||||
|  | ||||
|                 StringContent metricRequestJson = new StringContent(JsonSerializer.Serialize(metricRequest), | ||||
|                                                           Encoding.UTF8, | ||||
|                                                           Application.Json); | ||||
|  | ||||
|                 using HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(url, metricRequestJson); | ||||
|  | ||||
|                 success = httpResponseMessage.IsSuccessStatusCode; | ||||
|                 retries--; | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             StringBuilder errMsgBuilder = new(); | ||||
|             errMsgBuilder.Append($"An exception occurred when attempting to send MonIn count request for resource {_resource} "); | ||||
|             errMsgBuilder.Append($"with name {metricName} and value {metricValue} to url {url}. Exception: {ex.Message}"); | ||||
|             _logger.LogError(errMsgBuilder.ToString()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async void PostAverage(string metricName, double metricValue) { | ||||
|         string url = _baseUrl + "average"; | ||||
|  | ||||
|         StringBuilder logMsgBuilder = new(); | ||||
|         logMsgBuilder.Append($"Attempting to send MonIn average request for resource {_resource} "); | ||||
|         logMsgBuilder.Append($"with name {metricName} and value {metricValue} to url {url}"); | ||||
|         _logger.LogInformation(logMsgBuilder.ToString()); | ||||
|  | ||||
|         try { | ||||
|             bool success = false; | ||||
|             int retries = _retryLimit; | ||||
|             while (!success && retries > 0) { | ||||
|                 Task.Delay(TimeSpan.FromSeconds((_retryLimit - retries) * _backoffInSeconds)).Wait(); | ||||
|  | ||||
|                 HttpClient httpClient = _httpClientFactory.CreateClient(); | ||||
|  | ||||
|                 MonInMetricRequest metricRequest = new MonInMetricRequest() { | ||||
|                     resource = _resource, | ||||
|                     dateTime = DateTime.Now, | ||||
|                     metricName = metricName, | ||||
|                     metricValue = metricValue | ||||
|                 }; | ||||
|  | ||||
|                 StringContent metricRequestJson = new StringContent(JsonSerializer.Serialize(metricRequest), | ||||
|                                                           Encoding.UTF8, | ||||
|                                                           Application.Json); | ||||
|  | ||||
|                 using HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(url, metricRequestJson); | ||||
|  | ||||
|                 success = httpResponseMessage.IsSuccessStatusCode; | ||||
|                 retries--; | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             StringBuilder errMsgBuilder = new(); | ||||
|             errMsgBuilder.Append($"An exception occurred when attempting to send MonIn average request for resource {_resource} "); | ||||
|             errMsgBuilder.Append($"with name {metricName} and value {metricValue} to url {url}. Exception: {ex.Message}"); | ||||
|             _logger.LogError(errMsgBuilder.ToString()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	