From dda4a3f92b0788cb2f7856c857531b9d3ff50b7e Mon Sep 17 00:00:00 2001 From: Meysam Hadeli <35596795+meysamhadeli@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:57:37 +0330 Subject: [PATCH 1/2] refactor: refactor analyzer configs and add formating to cli --- .editorconfig | 1087 +++++++++++------ .github/actions/build/action.yml | 12 + Directory.Build.props | 4 +- README.md | 24 +- package.json | 1 + src/BuildingBlocks/BuildingBlocks.csproj | 4 +- src/BuildingBlocks/Core/EventDispatcher.cs | 10 +- src/BuildingBlocks/EFCore/Extensions.cs | 51 +- .../EventStoreDB/Events/EventTypeMapper.cs | 4 +- src/BuildingBlocks/Jwt/AuthHeaderHandler.cs | 2 +- src/BuildingBlocks/Logging/Extensions.cs | 3 +- .../Mongo/ImmutablePocoConvention.cs | 2 +- src/BuildingBlocks/Mongo/MongoDbContext.cs | 3 +- src/BuildingBlocks/TestBase/TestBase.cs | 477 +++++--- .../Web/SlugifyParameterTransformer.cs | 3 +- .../src/Flight/Data/Seed/InitialData.cs | 2 +- .../Flight/Features/GetFlightByIdTests.cs | 2 +- .../tests/UnitTest/Common/DbContextFactory.cs | 2 +- 18 files changed, 1076 insertions(+), 617 deletions(-) diff --git a/.editorconfig b/.editorconfig index b77a3d33..b388e303 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,208 +1,49 @@ -# See https://github.com/RehanSaeed/EditorConfig/releases for release notes. -# See https://github.com/RehanSaeed/EditorConfig for updates to this file. -# See http://EditorConfig.org for more information about .editorconfig files. +# https://editorconfig.org +# https://www.jetbrains.com/help/resharper/Using_EditorConfig.html +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/configuration-files +# When opening a file, EditorConfig plugins look for a file named .editorconfig in the directory of the opened file and in every parent directory. A search for .editorconfig files will stop if the root filepath is reached or an EditorConfig file with `root=true` is found. -########################################## -# Common Settings -########################################## +# Remove the line below if you want to inherit .editorconfig settings from higher directories + +################################################################################## +## https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ +## https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ +## Microsoft Rules +## -# This file is the top-most EditorConfig file root = true +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file -# All Files +# Global settings [*] charset = utf-8 -indent_style = space +end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true -########################################## -# File Extension Settings -########################################## - -# Visual Studio Solution Files -[*.sln] -indent_style = tab - -# Visual Studio XML Project Files -[*.{csproj,vbproj,vcxproj.filters,proj,projitems,shproj}] -indent_size = 2 - -# XML Configuration Files -[*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}] -indent_size = 2 - -# JSON Files -[*.{json,json5,webmanifest}] -indent_size = 2 - -# YAML Files -[*.{yml,yaml}] +# Xml project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_style = space indent_size = 2 -# Markdown Files -[*.{md,mdx}] -trim_trailing_whitespace = false - -# Web Files -[*.{htm,html,js,jsm,ts,tsx,cjs,cts,ctsx,mjs,mts,mtsx,css,sass,scss,less,pcss,svg,vue}] +# Xml config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_style = space indent_size = 2 -# Batch Files -[*.{cmd,bat}] -end_of_line = crlf +[*.{md,json}] +indent_style = space +indent_size = 4 -# Bash Files -[*.sh] -end_of_line = lf +[*.cs] +indent_style = space +indent_size = 4 +max_line_length = 100 -# Makefiles -[Makefile] -indent_style = tab - -########################################## -# Default .NET Code Style Severities -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/configuration-options#scope -########################################## - -[*.{cs,csx,cake,vb,vbx}] -# Default Severity for all .NET Code Style rules below -dotnet_analyzer_diagnostic.severity = warning - -########################################## -# Language Rules -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules -########################################## - -# .NET Style Rules -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#net-style-rules -[*.{cs,csx,cake,vb,vbx}] -# "this." and "Me." qualifiers -dotnet_style_qualification_for_field = false:silent -dotnet_style_qualification_for_property = false:silent -dotnet_style_qualification_for_method = false:silent -dotnet_style_qualification_for_event = false:silent -# Language keywords instead of framework type names for type references -dotnet_style_predefined_type_for_locals_parameters_members = true:warning -dotnet_style_predefined_type_for_member_access = true:warning -# Modifier preferences -dotnet_style_require_accessibility_modifiers = always:warning -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning -visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:warning -dotnet_style_readonly_field = true:warning -# Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:warning -# Expression-level preferences -dotnet_style_object_initializer = true:warning -dotnet_style_collection_initializer = true:warning -dotnet_style_explicit_tuple_names = true:warning -dotnet_style_prefer_inferred_tuple_names = true:warning -dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning -dotnet_style_prefer_auto_properties = true:warning -dotnet_style_prefer_conditional_expression_over_assignment = false:suggestion -dotnet_diagnostic.IDE0045.severity = suggestion -dotnet_style_prefer_conditional_expression_over_return = false:suggestion -dotnet_diagnostic.IDE0046.severity = suggestion -dotnet_style_prefer_compound_assignment = true:warning -dotnet_style_prefer_simplified_interpolation = true:warning -dotnet_style_prefer_simplified_boolean_expressions = true:warning -# Null-checking preferences -dotnet_style_coalesce_expression = true:warning -dotnet_style_null_propagation = true:warning -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning -# File header preferences -# file_header_template = \n© PROJECT-AUTHOR\n -# If you use StyleCop, you'll need to disable SA1636: File header copyright text should match. -# dotnet_diagnostic.SA1636.severity = none -# Undocumented -dotnet_style_operator_placement_when_wrapping = end_of_line:warning -csharp_style_prefer_null_check_over_type_check = true:warning - -# C# Style Rules -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#c-style-rules -[*.{cs,csx,cake}] -# 'var' preferences -csharp_style_var_for_built_in_types = true:warning -csharp_style_var_when_type_is_apparent = true:warning -csharp_style_var_elsewhere = true:warning -# Expression-bodied members -csharp_style_expression_bodied_methods = false:warning -csharp_style_expression_bodied_constructors = false:warning -csharp_style_expression_bodied_operators = false:silent -csharp_style_expression_bodied_properties = false:silent -csharp_style_expression_bodied_indexers = false:silent -csharp_style_expression_bodied_accessors = false:silent -csharp_style_expression_bodied_lambdas = false:silent -csharp_style_expression_bodied_local_functions = false:silent -# Pattern matching preferences -csharp_style_pattern_matching_over_is_with_cast_check = true:warning -csharp_style_pattern_matching_over_as_with_null_check = true:warning -csharp_style_prefer_switch_expression = true:warning -csharp_style_prefer_pattern_matching = true:warning -csharp_style_prefer_not_pattern = true:warning -# Expression-level preferences -csharp_style_inlined_variable_declaration = true:warning -csharp_prefer_simple_default_expression = true:warning -csharp_style_pattern_local_over_anonymous_function = true:warning -csharp_style_deconstructed_variable_declaration = true:warning -csharp_style_prefer_index_operator = true:warning -csharp_style_prefer_range_operator = true:warning -csharp_style_implicit_object_creation_when_type_is_apparent = true:warning -# "Null" checking preferences -csharp_style_throw_expression = true:warning -csharp_style_conditional_delegate_call = true:warning -# Code block preferences -csharp_prefer_braces = true:warning -csharp_prefer_simple_using_statement = true:suggestion -dotnet_diagnostic.IDE0063.severity = suggestion -# 'using' directive preferences -csharp_using_directive_placement = inside_namespace:warning -# Modifier preferences -csharp_prefer_static_local_function = true:warning - -########################################## -# Unnecessary Code Rules -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/unnecessary-code-rules -########################################## - -# .NET Unnecessary code rules -[*.{cs,csx,cake,vb,vbx}] -dotnet_code_quality_unused_parameters = all:warning -dotnet_remove_unnecessary_suppression_exclusions = none:warning - -# C# Unnecessary code rules -[*.{cs,csx,cake}] -csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion -dotnet_diagnostic.IDE0058.severity = suggestion -csharp_style_unused_value_assignment_preference = discard_variable:suggestion -dotnet_diagnostic.IDE0059.severity = suggestion - -########################################## -# Formatting Rules -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules -########################################## - -# .NET formatting rules -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#net-formatting-rules -[*.{cs,csx,cake,vb,vbx}] -# Organize using directives -dotnet_sort_system_directives_first = true -dotnet_separate_import_directive_groups = false -# Dotnet namespace options -dotnet_style_namespace_match_folder = false -dotnet_diagnostic.IDE0130.severity = none - -[*.{cs,vb}] -dotnet_diagnostic.CA1305.severity = none - -# C# formatting rules -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#c-formatting-rules -[*.{cs,csx,cake}] -# Newline options -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#new-line-options +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options +# New line preferences csharp_new_line_before_open_brace = all csharp_new_line_before_else = true csharp_new_line_before_catch = true @@ -210,215 +51,685 @@ csharp_new_line_before_finally = true csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_between_query_expression_clauses = true -# Indentation options -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#indentation-options -csharp_indent_case_contents = true -csharp_indent_switch_labels = true -csharp_indent_labels = no_change + +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options#indentation-options +# Indentation preferences csharp_indent_block_contents = true csharp_indent_braces = false -csharp_indent_case_contents_when_block = false -# Spacing options -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#spacing-options +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current + +# avoid this. unless absolutely necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# only use var when it's obvious what the variable type is +# csharp_style_var_for_built_in_types = false:none +# csharp_style_var_when_type_is_apparent = false:none +# csharp_style_var_elsewhere = false:suggestion + +# use language keywords instead of BCL types +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# name all constant fields using PascalCase +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static + +dotnet_naming_style.static_prefix_style.required_prefix = s_ +dotnet_naming_style.static_prefix_style.capitalization = camel_case + +# internal and private fields should be _camelCase +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style + +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal + +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +# Code style defaults +dotnet_sort_system_directives_first = true +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Pattern matching +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +# Null checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Space preferences csharp_space_after_cast = false -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_between_parentheses = false -csharp_space_before_colon_in_inheritance_clause = true csharp_space_after_colon_in_inheritance_clause = true -csharp_space_around_binary_operators = before_and_after -csharp_space_between_method_declaration_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_call_parameter_list_parentheses = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false csharp_space_after_comma = true -csharp_space_before_comma = false csharp_space_after_dot = false -csharp_space_before_dot = false +csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_semicolon_in_for_statement = true -csharp_space_before_semicolon_in_for_statement = false -csharp_space_around_declaration_statements = false +csharp_space_around_binary_operators = before_and_after +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 -# Wrap options -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#wrap-options -csharp_preserve_single_line_statements = false -csharp_preserve_single_line_blocks = true -# Namespace options -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#namespace-options -csharp_style_namespace_declarations = file_scoped:warning -########################################## -# .NET Naming Rules -# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/naming-rules -########################################## -[*.{cs,csx,cake,vb,vbx}] +################################################################################## +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ +################################################################################## +## Roslyn Code quality rules + +dotnet_diagnostic.CA1030.severity = none +dotnet_diagnostic.CA1034.severity = none +dotnet_diagnostic.CA1062.severity = suggestion +dotnet_code_quality.CA1062.exclude_extension_method_this_parameter = true +dotnet_code_quality.exclude_extension_method_this_parameter = true +dotnet_code_quality.null_check_validation_methods = ThrowIfArgumentIsNull +# CA1031: Do not catch general exception types +dotnet_diagnostic.CA1031.severity = none +# CA1303: Do not pass literals as localized parameters +dotnet_diagnostic.CA1303.severity = none +# CA1304: Specify CultureInfo +dotnet_diagnostic.CA1304.severity = error +# CA1307: Specify StringComparison for clarity +dotnet_diagnostic.CA1307.severity = error +# CA1308: Normalize strings to uppercase +dotnet_diagnostic.CA1308.severity = error +# CA1309: Use ordinal StringComparison +dotnet_diagnostic.CA1309.severity = error +# CA1724: Type names should not match namespaces +dotnet_diagnostic.CA1724.severity = none +# CA1819: Properties should not return arrays +dotnet_diagnostic.CA1819.severity = none +# CA1851: Possible multiple enumerations of IEnumerable collection. Related to GH-issue #2000 +dotnet_diagnostic.CA1851.severity = suggestion +# CA1859: Use concrete types when possible for improved performance +dotnet_diagnostic.CA1859.severity = suggestion +# CA1860: Avoid using 'Enumerable.Any()' extension method +dotnet_diagnostic.CA1860.severity = warning +# CA1861: Avoid constant arrays as arguments +dotnet_diagnostic.CA1861.severity = none +# CA2007: Do not directly await a Task +dotnet_diagnostic.CA2007.severity = none +# CA2225: Operator overloads have named alternates +dotnet_diagnostic.CA2225.severity = none +# CA3075: Insecure DTD Processing +dotnet_diagnostic.CA3075.severity = none +# CA5369: Use XmlReader for Deserialize +dotnet_diagnostic.CA5369.severity = none +# CA1305: Specify IFormatProvider +dotnet_diagnostic.CA1305.severity = None +# CA1063: Implement IDisposable correctly +dotnet_diagnostic.CA1063.severity = None +# CA2201: Do not raise reserved exception types +dotnet_diagnostic.ca2201.severity = Suggestion +# CA1848: Use the LoggerMessage delegates +dotnet_diagnostic.ca1848.severity = Suggestion +# CA1810: Initialize reference type static fields inline +dotnet_diagnostic.ca1810.severity = Suggestion +# CA1725: Parameter names should match base declaration +dotnet_diagnostic.ca1725.severity = Suggestion +# https://csharpier.com/docs/IntegratingWithLinters#code-analysis-rules + +################################################################################## +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ +################################################################################## +## Roslyn Code-style rules + +dotnet_diagnostic.IDE0048.severity = Suggestion +dotnet_diagnostic.IDE0028.severity = Suggestion +dotnet_diagnostic.IDE0029.severity = Suggestion +dotnet_diagnostic.IDE0030.severity = Suggestion +dotnet_diagnostic.IDE0004.severity = error + +# IDE0005: Remove unnecessary usings/imports +dotnet_diagnostic.IDE0005.severity = warning + +# IDE0051: Remove unused private members (no reads or writes) +dotnet_diagnostic.IDE0051.severity = Suggestion + +# IDE0052: Remove unread private members (writes but no reads) +dotnet_diagnostic.IDE0052.severity = error + +# IDE0055: Fix formatting +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/dotnet-formatting-options +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0055 +dotnet_diagnostic.IDE0055.severity = suggestion + +# CS1574: XML comment on 'construct' has syntactically incorrect cref attribute 'name' +dotnet_diagnostic.CS1574.severity = error + +################################################################################## +# https://jetbrains.com.xy2401.com/help/resharper/EditorConfig_Index.html +# https://jetbrains.com.xy2401.com/help/resharper/Reference__Code_Inspections_CSHARP.html +## Resharper + +# ReSharper properties +resharper_align_linq_query = true +resharper_align_multiline_array_and_object_initializer = true +resharper_align_multiline_binary_patterns = true +resharper_align_multiline_expression = true +resharper_align_multiline_extends_list = true +resharper_align_multiline_parameter = true +resharper_align_multiline_property_pattern = true +resharper_align_multiline_switch_expression = true +resharper_align_multiple_declaration = true +resharper_align_multline_type_parameter_constrains = true +resharper_align_multline_type_parameter_list = true +resharper_align_tuple_components = true +resharper_braces_for_for = required_for_multiline +resharper_braces_for_foreach = required_for_multiline +resharper_braces_for_ifelse = required_for_multiline +resharper_csharp_alignment_tab_fill_style = optimal_fill +resharper_csharp_indent_type_constraints = false +resharper_csharp_int_align_fix_in_adjacent = false +resharper_csharp_outdent_commas = true +resharper_csharp_stick_comment = false +resharper_csharp_wrap_after_declaration_lpar = true +resharper_csharp_wrap_after_invocation_lpar = true +resharper_csharp_wrap_arguments_style = chop_if_long +resharper_csharp_wrap_before_declaration_rpar = true +resharper_csharp_wrap_before_first_type_parameter_constraint = true +resharper_csharp_wrap_before_ternary_opsigns = false +resharper_csharp_wrap_extends_list_style = chop_if_long +resharper_csharp_wrap_multiple_declaration_style = wrap_if_long +resharper_csharp_wrap_multiple_type_parameter_constraints_style = chop_always +resharper_csharp_wrap_parameters_style = chop_if_long +resharper_enforce_line_ending_style = true +resharper_indent_anonymous_method_block = true +resharper_indent_braces_inside_statement_conditions = false +resharper_indent_nested_fixed_stmt = true +resharper_indent_nested_foreach_stmt = true +resharper_indent_nested_for_stmt = true +resharper_indent_nested_lock_stmt = true +resharper_indent_nested_usings_stmt = true +resharper_indent_nested_while_stmt = true +resharper_keep_existing_declaration_block_arrangement = true +resharper_keep_existing_declaration_parens_arrangement = false +resharper_keep_existing_embedded_arrangement = false +resharper_keep_existing_embedded_block_arrangement = true +resharper_keep_existing_enum_arrangement = true +resharper_keep_existing_invocation_parens_arrangement = false +resharper_keep_existing_property_patterns_arrangement = false +resharper_keep_existing_switch_expression_arrangement = false +resharper_max_array_initializer_elements_on_line = 700 +resharper_max_formal_parameters_on_line = 500 +resharper_max_invocation_arguments_on_line = 700 +resharper_new_line_before_while = true +resharper_place_attribute_on_same_line = false +resharper_place_linq_into_on_new_line = false +resharper_place_simple_case_statement_on_same_line = if_owner_is_single_line +resharper_place_simple_property_pattern_on_single_line = false +resharper_show_autodetect_configure_formatting_tip = false +resharper_space_within_single_line_array_initializer_braces = false +resharper_trailing_comma_in_multiline_lists = true +resharper_trailing_comma_in_singleline_lists = true +resharper_use_heuristics_for_body_style = false +resharper_use_indent_from_vs = false +resharper_use_roslyn_logic_for_evident_types = true +resharper_wrap_array_initializer_style = chop_always +resharper_wrap_chained_binary_expressions = chop_if_long +resharper_wrap_chained_binary_patterns = chop_if_long +resharper_wrap_chained_method_calls = chop_if_long +resharper_wrap_for_stmt_header_style = wrap_if_long +resharper_wrap_switch_expression = chop_if_long +resharper_wrap_verbatim_interpolated_strings = chop_if_long + +# ReSharper inspection severities +resharper_arrange_accessor_owner_body_highlighting = none +resharper_arrange_redundant_parentheses_highlighting = hint +resharper_arrange_type_member_modifiers_highlighting = hint +resharper_arrange_type_modifiers_highlighting = hint +resharper_check_namespace_highlighting = none +resharper_enforce_if_statement_braces_highlighting = hint +resharper_inconsistent_naming_highlighting = suggestion +resharper_static_member_in_generic_type_highlighting = none +resharper_suggest_var_or_type_built_in_types_highlighting = hint +resharper_suggest_var_or_type_elsewhere_highlighting = hint +resharper_suggest_var_or_type_simple_types_highlighting = hint +resharper_web_config_module_not_resolved_highlighting = warning +resharper_web_config_type_not_resolved_highlighting = warning +resharper_web_config_wrong_module_highlighting = warning + +# https://www.jetbrains.com/help/rider/ClassNeverInstantiated.Global.html +resharper_class_never_instantiated_global_highlighting = none + + +# Convert lambda expression to method group +resharper_convert_closure_to_method_group_highlighting = none + +# Start each element in a object or collection initializer on a new line +resharper_wrap_object_and_collection_initializer_style = chop_always + +# Force an empty line +resharper_blank_lines_after_multiline_statements = 1 + +# Don't remove existing line breaks +resharper_keep_existing_initializer_arrangement = true +resharper_keep_existing_arrangement = true + +# We care about that extra else after an else-if +resharper_redundant_if_else_block_highlighting = none + +# Don't remove explicit default cases in switch statements +resharper_redundant_empty_switch_section_highlighting = none + +resharper_align_multiline_binary_expressions_chain = false + +# Only use new() when the type is obvious +resharper_object_creation_when_type_not_evident = explicitly_typed +resharper_object_creation_when_type_evident = target_typed + +# Indent 4 spaces per necessary indention +resharper_continuous_indent_multiplier = 1 + +# Avoid breaking a generic definition +resharper_wrap_before_extends_colon = true + +resharper_blank_lines_before_multiline_statements = 1 + +resharper_parentheses_non_obvious_operations = arithmetic, multiplicative, equality, relational, additive +resharper_parentheses_redundancy_style = remove_if_not_clarifies_precedence + +################################################################################## +## https://github.com/bkoelman/CSharpGuidelinesAnalyzer +## CSharpGuidelines +################################################################################## + +dotnet_diagnostic.AV1561.max_parameter_count = 5 +# AV1008: Class should be non-static or its name should be suffixed with Extensions +dotnet_diagnostic.AV1008.severity = none +# AV1010: Type hides inherited member +dotnet_diagnostic.AV1010.severity = none +# AV1115: Member or local function contains the word 'and', which suggests doing multiple things +dotnet_diagnostic.AV1115.severity = suggestion +# AV1130: Return type in signature for Type should be a collection interface instead of a concrete type +dotnet_diagnostic.AV1130.severity = none +# AV1135: null is returned from method which has return type of string, collection or task +dotnet_diagnostic.AV1135.severity = none # re-enable if we can distinguish between string, collection and task +# AV1210: Catch a specific exception instead of Exception, SystemException or ApplicationException +dotnet_diagnostic.AV1210.severity = none +# AV1250: Evaluate LINQ query before returning it +dotnet_diagnostic.AV1250.severity = suggestion +# AV1500: Method 'CallerIdentifier.DetermineCallerIdentity()' contains 10 statements, which exceeds the maximum of 7 statements +dotnet_diagnostic.AV1500.severity = none +# AV1532: Loop statement contains nested loop +dotnet_diagnostic.AV1532.severity = suggestion +# AV1535: Missing block in case or default clause of switch statement +dotnet_diagnostic.AV1535.severity = none # re-enable if we can adjust the formatting to not indent the scope braces +# AV1537: If-else-if construct should end with an unconditional else clause +dotnet_diagnostic.AV1537.severity = suggestion +# AV1551: Method overload with the most parameters should be virtual +dotnet_diagnostic.AV1551.severity = none +# AV1555: Avoid using non-(nullable-)boolean named arguments +dotnet_diagnostic.AV1555.severity = suggestion +# AV1561: Method contains 5 parameters, which exceeds the maximum of 3 parameters +dotnet_diagnostic.AV1561.severity = suggestion +# AV1564: Parameter in public or internal member is of type bool or bool? +dotnet_diagnostic.AV1564.severity = suggestion +# AV1554: Do not use optional parameters in interface methods or their concrete implementations +dotnet_diagnostic.AV1554.severity = none +# AV1580: Argument for parameter calls nested method +dotnet_diagnostic.AV1580.severity = none +# AV1706: Parameter 'p' should have a more descriptive name +dotnet_diagnostic.AV1706.severity = warning +# AV1708: Type name contains term that should be avoided +dotnet_diagnostic.AV1708.severity = suggestion +# AV1710: Field contains the name of its containing type +dotnet_diagnostic.AV1710.severity = none +# AV2202: Replace call to Nullable.HasValue with null check +dotnet_diagnostic.AV2202.severity = none +# AV2305: Missing XML comment for internally visible type or member +dotnet_diagnostic.AV2305.severity = none +# AV2407: Region should be removed +dotnet_diagnostic.AV2407.severity = none + +################################################################################## +## https://github.com/DotNetAnalyzers/StyleCopAnalyzers/tree/master/documentation +## https://documentation.help/StyleCop/StyleCop.html +## StyleCop.Analyzers +################################################################################## -########################################## -# Styles -########################################## +# Using directive should appear within a namespace declaration +dotnet_diagnostic.sa1200.severity = None -# camel_case_style - Define the camelCase style -dotnet_naming_style.camel_case_style.capitalization = camel_case -# pascal_case_style - Define the PascalCase style -dotnet_naming_style.pascal_case_style.capitalization = pascal_case -# first_upper_style - The first character must start with an upper-case character -dotnet_naming_style.first_upper_style.capitalization = first_word_upper -# prefix_interface_with_i_style - Interfaces must be PascalCase and the first character of an interface must be an 'I' -dotnet_naming_style.prefix_interface_with_i_style.capitalization = pascal_case -dotnet_naming_style.prefix_interface_with_i_style.required_prefix = I -# prefix_type_parameters_with_t_style - Generic Type Parameters must be PascalCase and the first character must be a 'T' -dotnet_naming_style.prefix_type_parameters_with_t_style.capitalization = pascal_case -dotnet_naming_style.prefix_type_parameters_with_t_style.required_prefix = T -# disallowed_style - Anything that has this style applied is marked as disallowed -dotnet_naming_style.disallowed_style.capitalization = pascal_case -dotnet_naming_style.disallowed_style.required_prefix = ____RULE_VIOLATION____ -dotnet_naming_style.disallowed_style.required_suffix = ____RULE_VIOLATION____ -# internal_error_style - This style should never occur... if it does, it indicates a bug in file or in the parser using the file -dotnet_naming_style.internal_error_style.capitalization = pascal_case -dotnet_naming_style.internal_error_style.required_prefix = ____INTERNAL_ERROR____ -dotnet_naming_style.internal_error_style.required_suffix = ____INTERNAL_ERROR____ - -########################################## -# .NET Design Guideline Field Naming Rules -# Naming rules for fields follow the .NET Framework design guidelines -# https://docs.microsoft.com/dotnet/standard/design-guidelines/index -########################################## - -# All public/protected/protected_internal constant fields must be PascalCase -# https://docs.microsoft.com/dotnet/standard/design-guidelines/field -dotnet_naming_symbols.public_protected_constant_fields_group.applicable_accessibilities = public, protected, protected_internal -dotnet_naming_symbols.public_protected_constant_fields_group.required_modifiers = const -dotnet_naming_symbols.public_protected_constant_fields_group.applicable_kinds = field -dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.symbols = public_protected_constant_fields_group -dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.style = pascal_case_style -dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.severity = warning - -# All public/protected/protected_internal static readonly fields must be PascalCase -# https://docs.microsoft.com/dotnet/standard/design-guidelines/field -dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_accessibilities = public, protected, protected_internal -dotnet_naming_symbols.public_protected_static_readonly_fields_group.required_modifiers = static, readonly -dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_kinds = field -dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.symbols = public_protected_static_readonly_fields_group -dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style -dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.severity = warning - -# No other public/protected/protected_internal fields are allowed -# https://docs.microsoft.com/dotnet/standard/design-guidelines/field -dotnet_naming_symbols.other_public_protected_fields_group.applicable_accessibilities = public, protected, protected_internal -dotnet_naming_symbols.other_public_protected_fields_group.applicable_kinds = field -dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols = other_public_protected_fields_group -dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style = disallowed_style -dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity = none - -########################################## -# StyleCop Field Naming Rules -# Naming rules for fields follow the StyleCop analyzers -# This does not override any rules using disallowed_style above -# https://github.com/DotNetAnalyzers/StyleCopAnalyzers -########################################## - -# All constant fields must be PascalCase -# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md -dotnet_naming_symbols.stylecop_constant_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private -dotnet_naming_symbols.stylecop_constant_fields_group.required_modifiers = const -dotnet_naming_symbols.stylecop_constant_fields_group.applicable_kinds = field -dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.symbols = stylecop_constant_fields_group -dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.style = pascal_case_style -dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.severity = warning - -# All static readonly fields must be PascalCase -# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md -dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private -dotnet_naming_symbols.stylecop_static_readonly_fields_group.required_modifiers = static, readonly -dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_kinds = field -dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.symbols = stylecop_static_readonly_fields_group -dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style -dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.severity = warning - -# No non-private instance fields are allowed -# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md -dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected -dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds = field -dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols = stylecop_fields_must_be_private_group -dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style = disallowed_style -dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity = error - -# Private fields must be camelCase -# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md -dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.symbols = stylecop_private_fields_group -dotnet_naming_rule.private_members_with_underscore.symbols = private_fields -dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore -dotnet_naming_rule.private_members_with_underscore.severity = warning -dotnet_naming_symbols.private_fields.applicable_kinds = field -dotnet_naming_symbols.private_fields.applicable_accessibilities = private -dotnet_naming_style.prefix_underscore.capitalization = camel_case -dotnet_naming_style.prefix_underscore.required_prefix = _ - - -# Local variables must be camelCase -# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md -dotnet_naming_symbols.stylecop_local_fields_group.applicable_accessibilities = local -dotnet_naming_symbols.stylecop_local_fields_group.applicable_kinds = local -dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.symbols = stylecop_local_fields_group -dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.style = camel_case_style -dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.severity = silent - -# This rule should never fire. However, it's included for at least two purposes: -# First, it helps to understand, reason about, and root-case certain types of issues, such as bugs in .editorconfig parsers. -# Second, it helps to raise immediate awareness if a new field type is added (as occurred recently in C#). -dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_accessibilities = * -dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_kinds = field -dotnet_naming_rule.sanity_check_uncovered_field_case_rule.symbols = sanity_check_uncovered_field_case_group -dotnet_naming_rule.sanity_check_uncovered_field_case_rule.style = internal_error_style -dotnet_naming_rule.sanity_check_uncovered_field_case_rule.severity = error - -########################################## -# VSThread -########################################## -dotnet_diagnostic.VSTHRD111.severity = none - - -########################################## -# Other Naming Rules -########################################## - -# All of the following must be PascalCase: -# - Namespaces -# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-namespaces -# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md -# - Classes and Enumerations -# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces -# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md -# - Delegates -# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces#names-of-common-types -# - Constructors, Properties, Events, Methods -# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-type-members -dotnet_naming_symbols.element_group.applicable_kinds = namespace, class, enum, struct, delegate, event, method, property -dotnet_naming_rule.element_rule.symbols = element_group -dotnet_naming_rule.element_rule.style = pascal_case_style -dotnet_naming_rule.element_rule.severity = warning - -# Interfaces use PascalCase and are prefixed with uppercase 'I' -# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces -dotnet_naming_symbols.interface_group.applicable_kinds = interface -dotnet_naming_rule.interface_rule.symbols = interface_group -dotnet_naming_rule.interface_rule.style = prefix_interface_with_i_style -dotnet_naming_rule.interface_rule.severity = warning - -# Generics Type Parameters use PascalCase and are prefixed with uppercase 'T' -# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces -dotnet_naming_symbols.type_parameter_group.applicable_kinds = type_parameter -dotnet_naming_rule.type_parameter_rule.symbols = type_parameter_group -dotnet_naming_rule.type_parameter_rule.style = prefix_type_parameters_with_t_style -dotnet_naming_rule.type_parameter_rule.severity = warning - -# Function parameters use camelCase -# https://docs.microsoft.com/dotnet/standard/design-guidelines/naming-parameters -dotnet_naming_symbols.parameters_group.applicable_kinds = parameter -dotnet_naming_rule.parameters_rule.symbols = parameters_group -dotnet_naming_rule.parameters_rule.style = camel_case_style -dotnet_naming_rule.parameters_rule.severity = warning \ No newline at end of file +# Generic type parameter documentation should have text. +dotnet_diagnostic.sa1622.severity = None + +# XML comment analysis is disabled due to project configuration +dotnet_diagnostic.sa0001.severity = None + +# The file header is missing or not located at the top of the file +dotnet_diagnostic.sa1633.severity = None + +# Use string.Empty for empty strings +dotnet_diagnostic.sa1122.severity = None + +# Variable '_' should begin with lower-case letter +dotnet_diagnostic.sa1312.severity = None + +# Parameter '_' should begin with lower-case letter +dotnet_diagnostic.sa1313.severity = None + +# Elements should be documented +dotnet_diagnostic.sa1600.severity = None + +# Prefix local calls with this +dotnet_diagnostic.sa1101.severity = None + +# 'public' members should come before 'private' members +dotnet_diagnostic.sa1202.severity = None + +# Comments should contain text +dotnet_diagnostic.sa1120.severity = None + +# Constant fields should appear before non-constant fields +dotnet_diagnostic.sa1203.severity = None + +# Field '_blah' should not begin with an underscore +dotnet_diagnostic.sa1309.severity = None + +# Use trailing comma in multi-line initializers +dotnet_diagnostic.sa1413.severity = None + +# A method should not follow a class +dotnet_diagnostic.sa1201.severity = None + +# Elements should be separated by blank line +dotnet_diagnostic.sa1516.severity = None + +# The parameter spans multiple lines +dotnet_diagnostic.sa1118.severity = None + +# Static members should appear before non-static members +dotnet_diagnostic.sa1204.severity = None + +# Put constructor initializers on their own line +dotnet_diagnostic.sa1128.severity = None + +# Opening braces should not be preceded by blank line +dotnet_diagnostic.sa1509.severity = None + +# The parameter should begin on the line after the previous parameter +dotnet_diagnostic.sa1115.severity = None + +# File name should match first type name +dotnet_diagnostic.sa1649.severity = None + +# File may only contain a single type +dotnet_diagnostic.sa1402.severity = None + +# Enumeration items should be documented +dotnet_diagnostic.sa1602.severity = None + +# Element should not be on a single line +dotnet_diagnostic.sa1502.severity = None + +# Closing parenthesis should not be preceded by a space +dotnet_diagnostic.sa1009.severity = None + +# Closing parenthesis should be on line of last parameter +dotnet_diagnostic.sa1111.severity = None + +# Braces should not be ommitted +dotnet_diagnostic.sa1503.severity = None + +dotnet_diagnostic.sa1401.severity = None + +# The parameters to a C# method or indexer call or declaration are not all on the same line or each on a separate line. +# dotnet_diagnostic.SA1117.severity = Suggestion + +# The parameters to a C# method or indexer call or declaration span across multiple lines, but the first parameter does not start on the line after the opening bracket. +# dotnet_diagnostic.SA1116.severity = Suggestion + +# A C# partial element is missing a documentation header. +dotnet_diagnostic.sa1601.severity = None + +# A tag within a C# element’s documentation header is empty. +dotnet_diagnostic.sa1614.severity = None + +# A C# element is missing documentation for its return value. +dotnet_diagnostic.sa1615.severity = None + +# The tag within a C# element’s documentation header is empty. +dotnet_diagnostic.sa1616.severity = None + +# An opening brace within a C# element is not spaced correctly. +dotnet_diagnostic.sa1012.severity = Suggestion + +# A closing brace within a C# element is not spaced correctly. +dotnet_diagnostic.sa1013.severity = Suggestion + +# A call to an instance member of the local class or a base class is not prefixed with 'this.', within a C# code file. +dotnet_diagnostic.sa1101.severity = None + +# The keywords within the declaration of an element do not follow a standard ordering scheme. +dotnet_diagnostic.SA1206.severity = None + +# A single-line comment within C# code is not preceded by a blank line. +dotnet_diagnostic.SA1515.severity = Suggestion + +################################################################################## +## https://github.com/meziantou/Meziantou.Analyzer/tree/main/docs +## Meziantou.Analyzer + +# MA0049: Type name should not match containing namespace +dotnet_diagnostic.ma0049.severity = Suggestion + +# MA0048: File name must match type name +dotnet_diagnostic.ma0048.severity = Suggestion + +# MA0051: Method is too long +dotnet_diagnostic.ma0051.severity = Suggestion + +# https://www.meziantou.net/string-comparisons-are-harder-than-it-seems.htm +# MA0006 - Use String.Equals instead of equality operator +dotnet_diagnostic.ma0006.severity = Suggestion + +# MA0002 - IEqualityComparer or IComparer is missing +dotnet_diagnostic.ma0002.severity = Suggestion + +# MA0001 - StringComparison is missing +dotnet_diagnostic.ma0001.severity = Suggestion + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#13-pass-cancellation-token +# MA0040: Specify a cancellation token +dotnet_diagnostic.ma0032.severity = Suggestion + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#13-pass-cancellation-token +# MA0040: Flow the cancellation token when available +dotnet_diagnostic.ma0040.severity = Suggestion + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#14-using-cancellation-token-with-iasyncenumerable +# MA0079: Use a cancellation token using .WithCancellation() +dotnet_diagnostic.ma0079.severity = Suggestion + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#14-using-cancellation-token-with-iasyncenumerable +# MA0080: Use a cancellation token using .WithCancellation() +dotnet_diagnostic.ma0080.severity = Suggestion + +# Use Task.ConfigureAwait(false) as the current SynchronizationContext is not needed +dotnet_diagnostic.MA0004.severity = none + +# Add regex evaluation timeout +dotnet_diagnostic.MA0009.severity = none + +# Use an overload of 'ToString' that has a 'System.IFormatProvider' parameter. Already caught by CA1305. +dotnet_diagnostic.MA0011.severity = none + +# Use an overload of 'System.ArgumentException' with the parameter name. Just a suggestion since we have a bunch of justified exceptions. +dotnet_diagnostic.MA0015.severity = suggestion + +# Use an explicit StringComparer to compute hash codes +dotnet_diagnostic.MA0021.severity = none + +# Declare types in namespaces. Already caught by CA1050 +dotnet_diagnostic.MA0047.severity = none + +# Use an overload of 'GetHashCode' that has a StringComparison parameter +dotnet_diagnostic.MA0074.severity = none + +################################################################################## +## http://pihrt.net/Roslynator/Analyzers +## http://pihrt.net/Roslynator/Refactorings +## https://github.com/JosefPihrt/Roslynator/blob/main/docs/Configuration.md +## Roslynator + +# RCS1036 - Remove redundant empty line. +dotnet_diagnostic.rcs1036.severity = None + +# RCS1037 - Remove trailing white-space. +dotnet_diagnostic.rcs1037.severity = None + +# RCS1194: Implement exception constructors +dotnet_diagnostic.rcs1194.severity = None + +# https://cezarypiatek.github.io/post/async-analyzers-p1/#1-redundant-asyncawait +# RCS1174: Remove redundant async/await. +dotnet_diagnostic.rcs1174.severity = error + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#10-returning-null-from-a-task-returning-method +# RCS1210: Return Task.FromResult instead of returning null. +dotnet_diagnostic.rcs1210.severity = error + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#9-missing-configureawaitbool +# RCS1090: Call 'ConfigureAwait(false)'. +dotnet_diagnostic.rcs1090.severity = Suggestion + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#11-asynchronous-method-names-should-end-with-async +#RCS1046: Asynchronous method name should end with 'Async'. +dotnet_diagnostic.rcs1046.severity = Suggestion + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#12-non-asynchronous-method-names-shouldnt-end-with-async +# RCS1047: Non-asynchronous method name should not end with 'Async'. +dotnet_diagnostic.rcs1047.severity = error + +# https://github.com/JosefPihrt/Roslynator/blob/master/docs/analyzers/RCS1174.md +# RCS1174: Remove redundant async/await +dotnet_diagnostic.rcs1174.severity = Suggestion + + +# Combine 'Enumerable.Where' method chain. It doesn't make it more readable in all cases. +dotnet_diagnostic.RCS1112.severity = suggestion + +# Inline local variable. +dotnet_diagnostic.RCS1124.severity = suggestion + +# Add exception to documentation comment. Nice suggestion, but we don't want to document exceptions for internal code. +dotnet_diagnostic.RCS1140.severity = suggestion + +# Missing documentation +dotnet_diagnostic.RCS1141.severity = suggestion +dotnet_diagnostic.RCS1142.severity = suggestion + +# Use conditional access. Suggestion because it doesn't always improve readability +dotnet_diagnostic.RCS1146.severity = suggestion + +# Enum should declare explicit values. Disabled because we're not storing them. +dotnet_diagnostic.RCS1161.severity = none + +# Static member in generic type should use a type parameter. Disabled because it's not always applicable. +dotnet_diagnostic.RCS1158.severity = none + +# Add region name to #endregion. +dotnet_diagnostic.RCS1189.severity = none + +# Convert comment to documentation comment. Disabled because it also complains about SMELL/REFACTOR comments +dotnet_diagnostic.RCS1181.severity = none + +# Use Regex instance instead of static method. Disabled because it's not always worth it. +dotnet_diagnostic.RCS1186.severity = none + +# Use bit shift operator. +dotnet_diagnostic.RCS1237.severity = none + +# RCS1228: Unused element in documentation comment. (Equivalent to SA1614) +dotnet_diagnostic.RCS1228.severity = suggestion + +################################################################################## +## https://github.com/microsoft/vs-threading +## Microsoft.VisualStudio.Threading.Analyzers +################################################################################## + +# https://cezarypiatek.github.io/post/async-analyzers-p1/#2-calling-synchronous-method-inside-the-async-method +# VSTHRD103: Call async methods when in an async method +dotnet_diagnostic.vsthrd103.severity = Suggestion + + +# https://cezarypiatek.github.io/post/async-analyzers-p1/#3-async-void-method +# VSTHRD100: Avoid async void methods +dotnet_diagnostic.vsthrd100.severity = error + +# https://cezarypiatek.github.io/post/async-analyzers-p1/#4-unsupported-async-delegates +# VSTHRD101: Avoid unsupported async delegates +dotnet_diagnostic.vsthrd101.severity = error + +# https://cezarypiatek.github.io/post/async-analyzers-p1/#5-not-awaited-task-within-using-expression +# VSTHRD107: Await Task within using expression +dotnet_diagnostic.vsthrd107.severity = error + +# https://cezarypiatek.github.io/post/async-analyzers-p1/#7-unobserved-result-of-asynchronous-method +# VSTHRD110: Observe result of async calls +dotnet_diagnostic.vsthrd110.severity = Suggestion + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#8-synchronous-waits +# VSTHRD002: Avoid problematic synchronous waits +dotnet_diagnostic.vsthrd002.severity = Suggestion + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#9-missing-configureawaitbool +# VSTHRD111: Use ConfigureAwait(bool) +dotnet_diagnostic.vsthrd111.severity = Suggestion + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#10-returning-null-from-a-task-returning-method +# VSTHRD114: Avoid returning a null Task +dotnet_diagnostic.vsthrd114.severity = error + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#11-asynchronous-method-names-should-end-with-async +# VSTHRD200: Use "Async" suffix for async methods +dotnet_diagnostic.vsthrd200.severity = Suggestion + +# https://cezarypiatek.github.io/post/async-analyzers-p2/#12-non-asynchronous-method-names-shouldnt-end-with-async +# VSTHRD200: Use "Async" suffix for async methods +dotnet_diagnostic.vsthrd200.severity = Suggestion diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 066d207c..1422499f 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -50,6 +50,18 @@ runs: # restore root solution run: dotnet restore + # npm install, runs `prepare` script automatically in the initialize step + - name: Install NPM Dependencies + shell: bash + if: success() + run: npm install + + - name: Format Service + shell: bash + if: ${{ success()}} + run: | + npm run ci-format + - name: Build Service shell: bash if: ${{ success()}} diff --git a/Directory.Build.props b/Directory.Build.props index 50b29207..dcca6505 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -39,7 +39,9 @@ true true - All + false + latest-Recommended + Recommended diff --git a/README.md b/README.md index f897c653..b40dd661 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ - [Structure of Project](#structure-of-project) - [Development Setup](#development-setup) - [Dotnet Tools Packages](#dotnet-tools-packages) + - [Husky](#husky) + - [Upgrade Nuget Packages](#upgrade-nuget-packages) - [How to Run](#how-to-run) - [Config Certificate](#config-certificate) - [Docker Compose](#docker-compose) @@ -47,7 +49,7 @@ - :sparkle: Using `Postgres` for `write side` of some microservices. - :sparkle: Using `MongoDB` for `read side` of some microservices. - :sparkle: Using `Event Store` for `write side` of Booking-Microservice to store all `historical state` of aggregate. -- :sparkle: Using `Inbox Pattern` for ensuring message idempotency for receiver and `Exactly once Delivery`. +- :sparkle: Using `Inbox Pattern` for ensuring message idempotency for receiver and `Exactly once Delivery`. - :sparkle: Using `Outbox Pattern` for ensuring no message is lost and there is at `At Least One Delivery`. - :sparkle: Using `Unit Testing` for testing small units and mocking our dependencies with `Nsubstitute`. - :sparkle: Using `End-To-End Testing` and `Integration Testing` for testing `features` with all dependencies using `testcontainers`. @@ -159,7 +161,7 @@ Using the CQRS pattern, we cut each business functionality into vertical slices, ## Development Setup ### Dotnet Tools Packages -For installing our requirement package with .NET cli tools, we need to install `dotnet tool manifest`. +For installing our requirement packages with .NET cli tools, we need to install `dotnet tool manifest`. ```bash dotnet new tool-manifest ``` @@ -168,6 +170,24 @@ And after that we can restore our dotnet tools packages with .NET cli tools from dotnet tool restore ``` +### Husky +Here we use `husky` to handel some pre commit rules and we used `conventional commits` rules and `formatting` as pre commit rules, here in [package.json](./package.json). of course, we can add more rules for pre commit in future. (find more about husky in the [documentation](https://typicode.github.io/husky/get-started.html)) +We need to install `husky` package for `manage` `pre commits hooks` and also I add two packages `@commitlint/cli` and `@commitlint/config-conventional` for handling conventional commits rules in [package.json](./package.json). +Run the command bellow in the root of project to install all npm dependencies related to husky: + +```bash +npm install +``` + +> Note: In the root of project we have `.husky` folder and it has `commit-msg` file for handling conventional commits rules with provide user friendly message and `pre-commit` file that we can run our `scripts` as a `pre-commit` hooks. that here we call `format` script from [package.json](./package.json) for formatting purpose. + +### Upgrade Nuget Packages +For upgrading our nuget packages to last version, we use the great package [dotnet-outdated](https://github.com/dotnet-outdated/dotnet-outdated). +Run the command below in the root of project to upgrade all of packages to last version: +```bash +dotnet outdated -u +``` + ## How to Run > ### Config Certificate diff --git a/package.json b/package.json index f9e4d252..7ca936f2 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "scripts": { "prepare": "husky && dotnet tool restore", "format": "dotnet format booking-microservices-sample.sln --severity error --verbosity detailed", + "ci-format": "dotnet format booking-microservices-sample.sln --verify-no-changes --severity error --verbosity detailed", "upgrade-packages": "dotnet outdated --upgrade" }, "devDependencies": { diff --git a/src/BuildingBlocks/BuildingBlocks.csproj b/src/BuildingBlocks/BuildingBlocks.csproj index 2e403685..59523cd8 100644 --- a/src/BuildingBlocks/BuildingBlocks.csproj +++ b/src/BuildingBlocks/BuildingBlocks.csproj @@ -18,7 +18,7 @@ - + @@ -51,7 +51,7 @@ - + diff --git a/src/BuildingBlocks/Core/EventDispatcher.cs b/src/BuildingBlocks/Core/EventDispatcher.cs index 56daa4c7..36f278c0 100644 --- a/src/BuildingBlocks/Core/EventDispatcher.cs +++ b/src/BuildingBlocks/Core/EventDispatcher.cs @@ -54,13 +54,13 @@ await _persistMessageProcessor.PublishMessageAsync( switch (events) { case IReadOnlyList domainEvents: - { - var integrationEvents = await MapDomainEventToIntegrationEventAsync(domainEvents) + { + var integrationEvents = await MapDomainEventToIntegrationEventAsync(domainEvents) .ConfigureAwait(false); - await PublishIntegrationEvent(integrationEvents); - break; - } + await PublishIntegrationEvent(integrationEvents); + break; + } case IReadOnlyList integrationEvents: await PublishIntegrationEvent(integrationEvents); diff --git a/src/BuildingBlocks/EFCore/Extensions.cs b/src/BuildingBlocks/EFCore/Extensions.cs index 497ffbd5..11a21a87 100644 --- a/src/BuildingBlocks/EFCore/Extensions.cs +++ b/src/BuildingBlocks/EFCore/Extensions.cs @@ -16,36 +16,40 @@ namespace BuildingBlocks.EFCore; public static class Extensions { - public static IServiceCollection AddCustomDbContext( - this IServiceCollection services) - where TContext : DbContext, IDbContext + public static IServiceCollection AddCustomDbContext(this IServiceCollection services) + where TContext : DbContext, IDbContext { AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); services.AddValidateOptions(); - services.AddDbContext((sp, options) => - { - var postgresOptions = sp.GetRequiredService(); + services.AddDbContext( + (sp, options) => + { + var postgresOptions = sp.GetRequiredService(); - Guard.Against.Null(options, nameof(postgresOptions)); + Guard.Against.Null(options, nameof(postgresOptions)); - options.UseNpgsql(postgresOptions?.ConnectionString, - dbOptions => - { - dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name); - }) - // https://github.com/efcore/EFCore.NamingConventions - .UseSnakeCaseNamingConvention(); - }); + options.UseNpgsql( + postgresOptions?.ConnectionString, + dbOptions => + { + dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name); + }) + // https://github.com/efcore/EFCore.NamingConventions + .UseSnakeCaseNamingConvention(); + }); services.AddScoped(provider => provider.GetService()); return services; } - public static IApplicationBuilder UseMigration(this IApplicationBuilder app, IWebHostEnvironment env) - where TContext : DbContext, IDbContext + public static IApplicationBuilder UseMigration( + this IApplicationBuilder app, + IWebHostEnvironment env + ) + where TContext : DbContext, IDbContext { MigrateDatabaseAsync(app.ApplicationServices).GetAwaiter().GetResult(); @@ -62,13 +66,16 @@ public static IApplicationBuilder UseMigration(this IApplicationBuilde public static void FilterSoftDeletedProperties(this ModelBuilder modelBuilder) { Expression> filterExpr = e => !e.IsDeleted; + foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes() .Where(m => m.ClrType.IsAssignableTo(typeof(IEntity)))) { // modify expression to handle correct child type var parameter = Expression.Parameter(mutableEntityType.ClrType); + var body = ReplacingExpressionVisitor .Replace(filterExpr.Parameters.First(), parameter, filterExpr.Body); + var lambdaExpression = Expression.Lambda(body, parameter); // set filter @@ -76,8 +83,7 @@ public static void FilterSoftDeletedProperties(this ModelBuilder modelBuilder) } } - - //ref: https://andrewlock.net/customising-asp-net-core-identity-ef-core-naming-conventions-for-postgresql/ + // ref: https://andrewlock.net/customising-asp-net-core-identity-ef-core-naming-conventions-for-postgresql/ public static void ToSnakeCaseTables(this ModelBuilder modelBuilder) { foreach (var entity in modelBuilder.Model.GetEntityTypes()) @@ -86,7 +92,9 @@ public static void ToSnakeCaseTables(this ModelBuilder modelBuilder) entity.SetTableName(entity.GetTableName()?.Underscore()); var tableObjectIdentifier = - StoreObjectIdentifier.Table(entity.GetTableName()?.Underscore()!, entity.GetSchema()); + StoreObjectIdentifier.Table( + entity.GetTableName()?.Underscore()!, + entity.GetSchema()); // Replace column names foreach (var property in entity.GetProperties()) @@ -107,7 +115,7 @@ public static void ToSnakeCaseTables(this ModelBuilder modelBuilder) } private static async Task MigrateDatabaseAsync(IServiceProvider serviceProvider) - where TContext : DbContext, IDbContext + where TContext : DbContext, IDbContext { using var scope = serviceProvider.CreateScope(); @@ -119,6 +127,7 @@ private static async Task SeedDataAsync(IServiceProvider serviceProvider) { using var scope = serviceProvider.CreateScope(); var seeders = scope.ServiceProvider.GetServices(); + foreach (var seeder in seeders) { await seeder.SeedAllAsync(); diff --git a/src/BuildingBlocks/EventStoreDB/Events/EventTypeMapper.cs b/src/BuildingBlocks/EventStoreDB/Events/EventTypeMapper.cs index d8dbe8d5..28d2b3c8 100644 --- a/src/BuildingBlocks/EventStoreDB/Events/EventTypeMapper.cs +++ b/src/BuildingBlocks/EventStoreDB/Events/EventTypeMapper.cs @@ -22,7 +22,7 @@ public static void AddCustomMap(Type eventType, string mappedEventTypeName) public static string ToName(Type eventType) => Instance.typeNameMap.GetOrAdd(eventType, _ => { - var eventTypeName = eventType.FullName!.Replace(".", "_"); + var eventTypeName = eventType.FullName!.Replace(".", "_", StringComparison.CurrentCulture); Instance.typeMap.AddOrUpdate(eventTypeName, eventType, (_, _) => eventType); @@ -31,7 +31,7 @@ public static string ToName(Type eventType) => Instance.typeNameMap.GetOrAdd(eve public static Type? ToType(string eventTypeName) => Instance.typeMap.GetOrAdd(eventTypeName, _ => { - var type = TypeProvider.GetFirstMatchingTypeFromCurrentDomainAssembly(eventTypeName.Replace("_", ".")); + var type = TypeProvider.GetFirstMatchingTypeFromCurrentDomainAssembly(eventTypeName.Replace("_", ".", StringComparison.CurrentCulture)); if (type == null) return null; diff --git a/src/BuildingBlocks/Jwt/AuthHeaderHandler.cs b/src/BuildingBlocks/Jwt/AuthHeaderHandler.cs index 754cee82..ec4de799 100644 --- a/src/BuildingBlocks/Jwt/AuthHeaderHandler.cs +++ b/src/BuildingBlocks/Jwt/AuthHeaderHandler.cs @@ -17,7 +17,7 @@ protected override Task SendAsync(HttpRequestMessage reques { var token = (_httpContext?.HttpContext?.Request.Headers["Authorization"])?.ToString(); - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", "")); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token?.Replace("Bearer ", "", StringComparison.CurrentCulture)); return base.SendAsync(request, cancellationToken); } diff --git a/src/BuildingBlocks/Logging/Extensions.cs b/src/BuildingBlocks/Logging/Extensions.cs index b0cb4913..ed1813eb 100644 --- a/src/BuildingBlocks/Logging/Extensions.cs +++ b/src/BuildingBlocks/Logging/Extensions.cs @@ -1,3 +1,4 @@ +using System.Globalization; using System.Text; using BuildingBlocks.Web; using Microsoft.AspNetCore.Builder; @@ -44,7 +45,7 @@ public static WebApplicationBuilder AddCustomSerilog(this WebApplicationBuilder new ElasticsearchSinkOptions(new Uri(logOptions.Elastic.ElasticServiceUrl)) { AutoRegisterTemplate = true, - IndexFormat = $"{appOptions.Name}-{environment?.ToLower()}" + IndexFormat = $"{appOptions.Name}-{environment?.ToLower(CultureInfo.CurrentCulture)}" }); } diff --git a/src/BuildingBlocks/Mongo/ImmutablePocoConvention.cs b/src/BuildingBlocks/Mongo/ImmutablePocoConvention.cs index 55d52221..7052d41c 100644 --- a/src/BuildingBlocks/Mongo/ImmutablePocoConvention.cs +++ b/src/BuildingBlocks/Mongo/ImmutablePocoConvention.cs @@ -67,7 +67,7 @@ private static List GetMatchingProperties( private static bool ParameterMatchProperty(ParameterInfo parameter, PropertyInfo property) { - return string.Equals(property.Name, parameter.Name, System.StringComparison.InvariantCultureIgnoreCase) + return string.Equals(property.Name, parameter.Name, StringComparison.OrdinalIgnoreCase) && parameter.ParameterType == property.PropertyType; } diff --git a/src/BuildingBlocks/Mongo/MongoDbContext.cs b/src/BuildingBlocks/Mongo/MongoDbContext.cs index 4ef4e5ba..fbbce103 100644 --- a/src/BuildingBlocks/Mongo/MongoDbContext.cs +++ b/src/BuildingBlocks/Mongo/MongoDbContext.cs @@ -1,3 +1,4 @@ +using System.Globalization; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Bson.Serialization.Conventions; @@ -42,7 +43,7 @@ private static void RegisterConventions() public IMongoCollection GetCollection(string? name = null) { - return Database.GetCollection(name ?? typeof(T).Name.ToLower()); + return Database.GetCollection(name ?? typeof(T).Name.ToLower(CultureInfo.CurrentCulture)); } public void Dispose() diff --git a/src/BuildingBlocks/TestBase/TestBase.cs b/src/BuildingBlocks/TestBase/TestBase.cs index cfc33dbe..a339b814 100644 --- a/src/BuildingBlocks/TestBase/TestBase.cs +++ b/src/BuildingBlocks/TestBase/TestBase.cs @@ -37,7 +37,7 @@ namespace BuildingBlocks.TestBase; using Testcontainers.RabbitMq; public class TestFixture : IAsyncLifetime - where TEntryPoint : class +where TEntryPoint : class { private readonly WebApplicationFactory _factory; private int Timeout => 120; // Second @@ -60,10 +60,11 @@ public HttpClient HttpClient var claims = new Dictionary { - { ClaimTypes.Name, "test@sample.com" }, - { ClaimTypes.Role, "admin" }, + {ClaimTypes.Name, "test@sample.com"}, + {ClaimTypes.Role, "admin"}, {"scope", "flight-api"} }; + var httpClient = _factory?.CreateClient(); httpClient.SetFakeBearerToken(claims); return httpClient; @@ -71,7 +72,9 @@ public HttpClient HttpClient } public GrpcChannel Channel => - GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions { HttpClient = HttpClient }); + GrpcChannel.ForAddress( + HttpClient.BaseAddress!, + new GrpcChannelOptions { HttpClient = HttpClient }); public IServiceProvider ServiceProvider => _factory?.Services; public IConfiguration Configuration => _factory?.Services.GetRequiredService(); @@ -80,28 +83,36 @@ public HttpClient HttpClient protected TestFixture() { _factory = new WebApplicationFactory() - .WithWebHostBuilder(builder => - { - builder.ConfigureAppConfiguration(AddCustomAppSettings); - - builder.UseEnvironment("test"); - builder.ConfigureServices(services => + .WithWebHostBuilder( + builder => { - TestRegistrationServices?.Invoke(services); - services.ReplaceSingleton(AddHttpContextAccessorMock); - - services.AddSingleton(); - - // add authentication using a fake jwt bearer - we can use SetAdminUser method to set authenticate user to existing HttContextAccessor - // https://github.com/webmotions/fake-authentication-jwtbearer - // https://github.com/webmotions/fake-authentication-jwtbearer/issues/14 - services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = FakeJwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = FakeJwtBearerDefaults.AuthenticationScheme; - }).AddFakeJwtBearer(); + builder.ConfigureAppConfiguration(AddCustomAppSettings); + + builder.UseEnvironment("test"); + + builder.ConfigureServices( + services => + { + TestRegistrationServices?.Invoke(services); + services.ReplaceSingleton(AddHttpContextAccessorMock); + + services.AddSingleton(); + + // add authentication using a fake jwt bearer - we can use SetAdminUser method to set authenticate user to existing HttContextAccessor + // https://github.com/webmotions/fake-authentication-jwtbearer + // https://github.com/webmotions/fake-authentication-jwtbearer/issues/14 + services.AddAuthentication( + options => + { + options.DefaultAuthenticateScheme = + FakeJwtBearerDefaults.AuthenticationScheme; + + options.DefaultChallengeScheme = + FakeJwtBearerDefaults.AuthenticationScheme; + }) + .AddFakeJwtBearer(); + }); }); - }); } public async Task InitializeAsync() @@ -114,7 +125,7 @@ public async Task DisposeAsync() { await StopTestContainerAsync(); await _factory.DisposeAsync(); - CancellationTokenSource.Cancel(); + await CancellationTokenSource.CancelAsync(); } public virtual void RegisterServices(Action services) @@ -153,87 +164,123 @@ protected async Task ExecuteScopeAsync(Func> act public Task SendAsync(IRequest request) { - return ExecuteScopeAsync(sp => - { - var mediator = sp.GetRequiredService(); + return ExecuteScopeAsync( + sp => + { + var mediator = sp.GetRequiredService(); - return mediator.Send(request); - }); + return mediator.Send(request); + }); } public Task SendAsync(IRequest request) { - return ExecuteScopeAsync(sp => - { - var mediator = sp.GetRequiredService(); - return mediator.Send(request); - }); + return ExecuteScopeAsync( + sp => + { + var mediator = sp.GetRequiredService(); + return mediator.Send(request); + }); } - public async Task Publish(TMessage message, CancellationToken cancellationToken = default) - where TMessage : class, IEvent + public async Task Publish( + TMessage message, + CancellationToken cancellationToken = default + ) + where TMessage : class, IEvent { await TestHarness.Bus.Publish(message, cancellationToken); } - public async Task WaitForPublishing(CancellationToken cancellationToken = default) - where TMessage : class, IEvent + public async Task WaitForPublishing( + CancellationToken cancellationToken = default + ) + where TMessage : class, IEvent { - var result = await WaitUntilConditionMet(async () => - { - var published = await TestHarness.Published.Any(cancellationToken); - var faulty = await TestHarness.Published.Any>(cancellationToken); - return published && faulty == false; - }); - return result; - } + var result = await WaitUntilConditionMet( + async () => + { + var published = + await TestHarness.Published.Any(cancellationToken); - public async Task WaitForConsuming(CancellationToken cancellationToken = default) - where TMessage : class, IEvent - { - var result = await WaitUntilConditionMet(async () => - { - var consumed = await TestHarness.Consumed.Any(cancellationToken); - var faulty = await TestHarness.Consumed.Any>(cancellationToken); + var faulty = + await TestHarness.Published.Any>( + cancellationToken); - return consumed && faulty == false; - }); + return published && faulty == false; + }); return result; } - public async Task ShouldProcessedPersistInternalCommand( - CancellationToken cancellationToken = default) - where TInternalCommand : class, IInternalCommand + public async Task WaitForConsuming( + CancellationToken cancellationToken = default + ) + where TMessage : class, IEvent { - var result = await WaitUntilConditionMet(async () => - { - return await ExecuteScopeAsync(async sp => - { - var persistMessageProcessor = sp.GetService(); - Guard.Against.Null(persistMessageProcessor, nameof(persistMessageProcessor)); + var result = await WaitUntilConditionMet( + async () => + { + var consumed = + await TestHarness.Consumed.Any(cancellationToken); - var filter = await persistMessageProcessor.GetByFilterAsync(x => - x.DeliveryType == MessageDeliveryType.Internal && - typeof(TInternalCommand).ToString() == x.DataType); + var faulty = + await TestHarness.Consumed.Any>(cancellationToken); - var res = filter.Any(x => x.MessageStatus == MessageStatus.Processed); + return consumed && faulty == false; + }); - return res; - }); - }); + return result; + } + + public async Task ShouldProcessedPersistInternalCommand( + CancellationToken cancellationToken = default + ) + where TInternalCommand : class, IInternalCommand + { + var result = await WaitUntilConditionMet( + async () => + { + return await ExecuteScopeAsync( + async sp => + { + var persistMessageProcessor = + sp.GetService(); + + Guard.Against.Null( + persistMessageProcessor, + nameof(persistMessageProcessor)); + + var filter = + await persistMessageProcessor.GetByFilterAsync( + x => + x.DeliveryType == + MessageDeliveryType.Internal && + typeof(TInternalCommand).ToString() == + x.DataType); + + var res = filter.Any( + x => x.MessageStatus == MessageStatus.Processed); + + return res; + }); + }); return result; } // Ref: https://tech.energyhelpline.com/in-memory-testing-with-masstransit/ - private async Task WaitUntilConditionMet(Func> conditionToMet, int? timeoutSecond = null) + private async Task WaitUntilConditionMet( + Func> conditionToMet, + int? timeoutSecond = null + ) { var time = timeoutSecond ?? Timeout; var startTime = DateTime.Now; var timeoutExpired = false; var meet = await conditionToMet.Invoke(); + while (!meet) { if (timeoutExpired) @@ -275,27 +322,42 @@ private async Task StopTestContainerAsync() private void AddCustomAppSettings(IConfigurationBuilder configuration) { - configuration.AddInMemoryCollection(new KeyValuePair[] - { - new("PostgresOptions:ConnectionString", PostgresTestcontainer.GetConnectionString()), - new("PersistMessageOptions:ConnectionString", PostgresPersistTestContainer.GetConnectionString()), - new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname), - new("RabbitMqOptions:UserName", TestContainers.RabbitMqContainerConfiguration.UserName), - new("RabbitMqOptions:Password", TestContainers.RabbitMqContainerConfiguration.Password), new( - "RabbitMqOptions:Port", - RabbitMqTestContainer.GetMappedPublicPort(TestContainers.RabbitMqContainerConfiguration.Port) - .ToString(NumberFormatInfo.InvariantInfo)), - new("MongoOptions:ConnectionString", MongoDbTestContainer.GetConnectionString()), - new("MongoOptions:DatabaseName", TestContainers.MongoContainerConfiguration.Name), - new("EventStoreOptions:ConnectionString", EventStoreDbTestContainer.GetConnectionString()) - }); + configuration.AddInMemoryCollection( + new KeyValuePair[] + { + new( + "PostgresOptions:ConnectionString", + PostgresTestcontainer.GetConnectionString()), + new( + "PersistMessageOptions:ConnectionString", + PostgresPersistTestContainer.GetConnectionString()), + new("RabbitMqOptions:HostName", RabbitMqTestContainer.Hostname), + new( + "RabbitMqOptions:UserName", + TestContainers.RabbitMqContainerConfiguration.UserName), + new( + "RabbitMqOptions:Password", + TestContainers.RabbitMqContainerConfiguration.Password), + new( + "RabbitMqOptions:Port", + RabbitMqTestContainer.GetMappedPublicPort( + TestContainers.RabbitMqContainerConfiguration.Port) + .ToString(NumberFormatInfo.InvariantInfo)), + new("MongoOptions:ConnectionString", MongoDbTestContainer.GetConnectionString()), + new("MongoOptions:DatabaseName", TestContainers.MongoContainerConfiguration.Name), + new( + "EventStoreOptions:ConnectionString", + EventStoreDbTestContainer.GetConnectionString()) + }); } private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider serviceProvider) { var httpContextAccessorMock = Substitute.For(); using var scope = serviceProvider.CreateScope(); - httpContextAccessorMock.HttpContext = new DefaultHttpContext { RequestServices = scope.ServiceProvider }; + + httpContextAccessorMock.HttpContext = new DefaultHttpContext + { RequestServices = scope.ServiceProvider }; httpContextAccessorMock.HttpContext.Request.Host = new HostString("localhost", 6012); httpContextAccessorMock.HttpContext.Request.Scheme = "http"; @@ -305,8 +367,8 @@ private IHttpContextAccessor AddHttpContextAccessorMock(IServiceProvider service } public class TestWriteFixture : TestFixture - where TEntryPoint : class - where TWContext : DbContext +where TEntryPoint : class +where TWContext : DbContext { public Task ExecuteDbContextAsync(Func action) { @@ -320,7 +382,8 @@ public Task ExecuteDbContextAsync(Func action) public Task ExecuteDbContextAsync(Func action) { - return ExecuteScopeAsync(sp => action(sp.GetService(), sp.GetService())); + return ExecuteScopeAsync( + sp => action(sp.GetService(), sp.GetService())); } public Task ExecuteDbContextAsync(Func> action) @@ -335,94 +398,110 @@ public Task ExecuteDbContextAsync(Func> action) public Task ExecuteDbContextAsync(Func> action) { - return ExecuteScopeAsync(sp => action(sp.GetService(), sp.GetService())); + return ExecuteScopeAsync( + sp => action(sp.GetService(), sp.GetService())); } - public Task InsertAsync(params T[] entities) where T : class + public Task InsertAsync(params T[] entities) + where T : class { - return ExecuteDbContextAsync(db => - { - foreach (var entity in entities) + return ExecuteDbContextAsync( + db => { - db.Set().Add(entity); - } + foreach (var entity in entities) + { + db.Set().Add(entity); + } - return db.SaveChangesAsync(); - }); + return db.SaveChangesAsync(); + }); } - public async Task InsertAsync(TEntity entity) where TEntity : class + public async Task InsertAsync(TEntity entity) + where TEntity : class { - await ExecuteDbContextAsync(db => - { - db.Set().Add(entity); + await ExecuteDbContextAsync( + db => + { + db.Set().Add(entity); - return db.SaveChangesAsync(); - }); + return db.SaveChangesAsync(); + }); } public Task InsertAsync(TEntity entity, TEntity2 entity2) - where TEntity : class - where TEntity2 : class + where TEntity : class + where TEntity2 : class { - return ExecuteDbContextAsync(db => - { - db.Set().Add(entity); - db.Set().Add(entity2); + return ExecuteDbContextAsync( + db => + { + db.Set().Add(entity); + db.Set().Add(entity2); - return db.SaveChangesAsync(); - }); + return db.SaveChangesAsync(); + }); } - public Task InsertAsync(TEntity entity, TEntity2 entity2, TEntity3 entity3) - where TEntity : class - where TEntity2 : class - where TEntity3 : class + public Task InsertAsync( + TEntity entity, + TEntity2 entity2, + TEntity3 entity3 + ) + where TEntity : class + where TEntity2 : class + where TEntity3 : class { - return ExecuteDbContextAsync(db => - { - db.Set().Add(entity); - db.Set().Add(entity2); - db.Set().Add(entity3); + return ExecuteDbContextAsync( + db => + { + db.Set().Add(entity); + db.Set().Add(entity2); + db.Set().Add(entity3); - return db.SaveChangesAsync(); - }); + return db.SaveChangesAsync(); + }); } - public Task InsertAsync(TEntity entity, TEntity2 entity2, TEntity3 entity3, - TEntity4 entity4) - where TEntity : class - where TEntity2 : class - where TEntity3 : class - where TEntity4 : class + public Task InsertAsync( + TEntity entity, + TEntity2 entity2, + TEntity3 entity3, + TEntity4 entity4 + ) + where TEntity : class + where TEntity2 : class + where TEntity3 : class + where TEntity4 : class { - return ExecuteDbContextAsync(db => - { - db.Set().Add(entity); - db.Set().Add(entity2); - db.Set().Add(entity3); - db.Set().Add(entity4); + return ExecuteDbContextAsync( + db => + { + db.Set().Add(entity); + db.Set().Add(entity2); + db.Set().Add(entity3); + db.Set().Add(entity4); - return db.SaveChangesAsync(); - }); + return db.SaveChangesAsync(); + }); } public Task FindAsync(TKey id) - where T : class, IEntity + where T : class, IEntity { return ExecuteDbContextAsync(db => db.Set().FindAsync(id).AsTask()); } public Task FirstOrDefaultAsync() - where T : class, IEntity + where T : class, IEntity { return ExecuteDbContextAsync(db => db.Set().FirstOrDefaultAsync()); } } public class TestReadFixture : TestFixture - where TEntryPoint : class - where TRContext : MongoDbContext +where TEntryPoint : class +where TRContext : MongoDbContext { public Task ExecuteReadContextAsync(Func action) { @@ -434,19 +513,22 @@ public Task ExecuteReadContextAsync(Func> action) return ExecuteScopeAsync(sp => action(sp.GetRequiredService())); } - public async Task InsertMongoDbContextAsync(string collectionName, params T[] entities) where T : class + public async Task InsertMongoDbContextAsync(string collectionName, params T[] entities) + where T : class { - await ExecuteReadContextAsync(async db => - { - await db.GetCollection(collectionName).InsertManyAsync(entities.ToList()); - }); + await ExecuteReadContextAsync( + async db => + { + await db.GetCollection(collectionName).InsertManyAsync(entities.ToList()); + }); } } -public class TestFixture : TestWriteFixture - where TEntryPoint : class - where TWContext : DbContext - where TRContext : MongoDbContext +public class TestFixture + : TestWriteFixture +where TEntryPoint : class +where TWContext : DbContext +where TRContext : MongoDbContext { public Task ExecuteReadContextAsync(Func action) { @@ -458,17 +540,19 @@ public Task ExecuteReadContextAsync(Func> action) return ExecuteScopeAsync(sp => action(sp.GetRequiredService())); } - public async Task InsertMongoDbContextAsync(string collectionName, params T[] entities) where T : class + public async Task InsertMongoDbContextAsync(string collectionName, params T[] entities) + where T : class { - await ExecuteReadContextAsync(async db => - { - await db.GetCollection(collectionName).InsertManyAsync(entities.ToList()); - }); + await ExecuteReadContextAsync( + async db => + { + await db.GetCollection(collectionName).InsertManyAsync(entities.ToList()); + }); } } public class TestFixtureCore : IAsyncLifetime - where TEntryPoint : class +where TEntryPoint : class { private Respawner _reSpawnerDefaultDb; private Respawner _reSpawnerPersistDb; @@ -476,7 +560,10 @@ public class TestFixtureCore : IAsyncLifetime private NpgsqlConnection PersistDbConnection { get; set; } - public TestFixtureCore(TestFixture integrationTestFixture, ITestOutputHelper outputHelper) + public TestFixtureCore( + TestFixture integrationTestFixture, + ITestOutputHelper outputHelper + ) { Fixture = integrationTestFixture; integrationTestFixture.RegisterServices(RegisterTestsServices); @@ -505,13 +592,15 @@ private async Task InitPostgresAsync() if (!string.IsNullOrEmpty(persistOptions?.ConnectionString)) { - await Fixture.PersistMessageBackgroundService.StartAsync(Fixture.CancellationTokenSource.Token); + await Fixture.PersistMessageBackgroundService.StartAsync( + Fixture.CancellationTokenSource.Token); PersistDbConnection = new NpgsqlConnection(persistOptions.ConnectionString); await PersistDbConnection.OpenAsync(); - _reSpawnerPersistDb = await Respawner.CreateAsync(PersistDbConnection, - new RespawnerOptions { DbAdapter = DbAdapter.Postgres }); + _reSpawnerPersistDb = await Respawner.CreateAsync( + PersistDbConnection, + new RespawnerOptions { DbAdapter = DbAdapter.Postgres }); } if (!string.IsNullOrEmpty(postgresOptions?.ConnectionString)) @@ -519,8 +608,9 @@ private async Task InitPostgresAsync() DefaultDbConnection = new NpgsqlConnection(postgresOptions.ConnectionString); await DefaultDbConnection.OpenAsync(); - _reSpawnerDefaultDb = await Respawner.CreateAsync(DefaultDbConnection, - new RespawnerOptions { DbAdapter = DbAdapter.Postgres }); + _reSpawnerDefaultDb = await Respawner.CreateAsync( + DefaultDbConnection, + new RespawnerOptions { DbAdapter = DbAdapter.Postgres }); await SeedDataAsync(); } @@ -532,7 +622,8 @@ private async Task ResetPostgresAsync() { await _reSpawnerPersistDb.ResetAsync(PersistDbConnection); - await Fixture.PersistMessageBackgroundService.StopAsync(Fixture.CancellationTokenSource.Token); + await Fixture.PersistMessageBackgroundService.StopAsync( + Fixture.CancellationTokenSource.Token); } if (DefaultDbConnection is not null) @@ -545,8 +636,10 @@ private async Task ResetMongoAsync(CancellationToken cancellationToken = default { //https://stackoverflow.com/questions/3366397/delete-everything-in-a-mongodb-database var dbClient = new MongoClient(Fixture.MongoDbTestContainer?.GetConnectionString()); - var collections = await dbClient.GetDatabase(TestContainers.MongoContainerConfiguration.Name) - .ListCollectionsAsync(cancellationToken: cancellationToken); + + var collections = await dbClient + .GetDatabase(TestContainers.MongoContainerConfiguration.Name) + .ListCollectionsAsync(cancellationToken: cancellationToken); foreach (var collection in collections.ToList()) { @@ -557,27 +650,30 @@ await dbClient.GetDatabase(TestContainers.MongoContainerConfiguration.Name) private async Task ResetRabbitMqAsync(CancellationToken cancellationToken = default) { - var port = Fixture.RabbitMqTestContainer?.GetMappedPublicPort(TestContainers.RabbitMqContainerConfiguration - .ApiPort) - ?? TestContainers.RabbitMqContainerConfiguration.ApiPort; + var port = Fixture.RabbitMqTestContainer?.GetMappedPublicPort( + TestContainers.RabbitMqContainerConfiguration + .ApiPort) ?? + TestContainers.RabbitMqContainerConfiguration.ApiPort; var managementClient = new ManagementClient(Fixture.RabbitMqTestContainer?.Hostname, TestContainers.RabbitMqContainerConfiguration?.UserName, TestContainers.RabbitMqContainerConfiguration?.Password, port); var bd = await managementClient.GetBindingsAsync(cancellationToken); - var bindings = bd.Where(x => !string.IsNullOrEmpty(x.Source) && !string.IsNullOrEmpty(x.Destination)); + + var bindings = bd.Where( + x => !string.IsNullOrEmpty(x.Source) && !string.IsNullOrEmpty(x.Destination)); foreach (var binding in bindings) { await managementClient.DeleteBindingAsync(binding, cancellationToken); } - var queues = await managementClient.GetQueuesAsync(cancellationToken); + var queues = await managementClient.GetQueuesAsync(cancellationToken: cancellationToken); foreach (var queue in queues) { - await managementClient.DeleteQueueAsync(queue, cancellationToken); + await managementClient.PurgeAsync(queue, cancellationToken); } } @@ -590,6 +686,7 @@ private async Task SeedDataAsync() using var scope = Fixture.ServiceProvider.CreateScope(); var seeders = scope.ServiceProvider.GetServices(); + foreach (var seeder in seeders) { await seeder.SeedAllAsync(); @@ -598,13 +695,14 @@ private async Task SeedDataAsync() } public abstract class TestReadBase : TestFixtureCore - //,IClassFixture> - where TEntryPoint : class - where TRContext : MongoDbContext +// ,IClassFixture> +where TEntryPoint : class +where TRContext : MongoDbContext { protected TestReadBase( - TestReadFixture integrationTestFixture, ITestOutputHelper outputHelper = null) : base( - integrationTestFixture, outputHelper) + TestReadFixture integrationTestFixture, + ITestOutputHelper outputHelper = null + ) : base(integrationTestFixture, outputHelper) { Fixture = integrationTestFixture; } @@ -613,13 +711,14 @@ protected TestReadBase( } public abstract class TestWriteBase : TestFixtureCore - //,IClassFixture> - where TEntryPoint : class - where TWContext : DbContext +//,IClassFixture> +where TEntryPoint : class +where TWContext : DbContext { protected TestWriteBase( - TestWriteFixture integrationTestFixture, ITestOutputHelper outputHelper = null) : base( - integrationTestFixture, outputHelper) + TestWriteFixture integrationTestFixture, + ITestOutputHelper outputHelper = null + ) : base(integrationTestFixture, outputHelper) { Fixture = integrationTestFixture; } @@ -628,13 +727,15 @@ protected TestWriteBase( } public abstract class TestBase : TestFixtureCore - //,IClassFixture> - where TEntryPoint : class - where TWContext : DbContext - where TRContext : MongoDbContext +//,IClassFixture> +where TEntryPoint : class +where TWContext : DbContext +where TRContext : MongoDbContext { protected TestBase( - TestFixture integrationTestFixture, ITestOutputHelper outputHelper = null) : + TestFixture integrationTestFixture, + ITestOutputHelper outputHelper = null + ) : base(integrationTestFixture, outputHelper) { Fixture = integrationTestFixture; diff --git a/src/BuildingBlocks/Web/SlugifyParameterTransformer.cs b/src/BuildingBlocks/Web/SlugifyParameterTransformer.cs index 9aecf771..29bc834a 100644 --- a/src/BuildingBlocks/Web/SlugifyParameterTransformer.cs +++ b/src/BuildingBlocks/Web/SlugifyParameterTransformer.cs @@ -1,3 +1,4 @@ +using System.Globalization; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Routing; @@ -10,6 +11,6 @@ public string TransformOutbound(object value) // Slugify value return value == null ? null - : Regex.Replace(value.ToString() ?? string.Empty, "([a-z])([A-Z])", "$1-$2").ToLower(); + : Regex.Replace(value.ToString() ?? string.Empty, "([a-z])([A-Z])", "$1-$2").ToLower(CultureInfo.CurrentCulture); } } diff --git a/src/Services/Flight/src/Flight/Data/Seed/InitialData.cs b/src/Services/Flight/src/Flight/Data/Seed/InitialData.cs index 70b5494f..695251ee 100644 --- a/src/Services/Flight/src/Flight/Data/Seed/InitialData.cs +++ b/src/Services/Flight/src/Flight/Data/Seed/InitialData.cs @@ -45,7 +45,7 @@ static InitialData() ArriveDate.Of(new DateTime(2022, 1, 31, 14, 0, 0)), AirportId.Of(Airports.Last().Id), DurationMinutes.Of(120m), FlightDate.Of(new DateTime(2022, 1, 31, 13, 0, 0)), global::Flight.Flights.Enums.FlightStatus.Completed, - Price.Of((decimal)8000)) + Price.Of(8000)) }; Seats = new List diff --git a/src/Services/Flight/tests/EndToEndTest/Flight/Features/GetFlightByIdTests.cs b/src/Services/Flight/tests/EndToEndTest/Flight/Features/GetFlightByIdTests.cs index 232d7387..057e03a0 100644 --- a/src/Services/Flight/tests/EndToEndTest/Flight/Features/GetFlightByIdTests.cs +++ b/src/Services/Flight/tests/EndToEndTest/Flight/Features/GetFlightByIdTests.cs @@ -25,7 +25,7 @@ public async Task should_retrive_a_flight_by_id_currectly() await Fixture.SendAsync(command); // Act - var route = ApiRoutes.Flight.GetFlightById.Replace(ApiRoutes.Flight.Id, command.Id.ToString()); + var route = ApiRoutes.Flight.GetFlightById.Replace(ApiRoutes.Flight.Id, command.Id.ToString(), StringComparison.CurrentCulture); var result = await Fixture.HttpClient.GetAsync(route); // Assert diff --git a/src/Services/Flight/tests/UnitTest/Common/DbContextFactory.cs b/src/Services/Flight/tests/UnitTest/Common/DbContextFactory.cs index 69abdb82..b3d30c7e 100644 --- a/src/Services/Flight/tests/UnitTest/Common/DbContextFactory.cs +++ b/src/Services/Flight/tests/UnitTest/Common/DbContextFactory.cs @@ -65,7 +65,7 @@ private static void FlightDataSeeder(FlightDbContext context) ArriveDate.Of( new DateTime(2022, 1, 31, 14, 0, 0)), AirportId.Of( _airportId2), DurationMinutes.Of(120m), FlightDate.Of( new DateTime(2022, 1, 31)), FlightStatus.Completed, - Price.Of((decimal)8000)) + Price.Of(8000)) }; context.Flights.AddRange(flights); From fa3b1842a8c5f1a97c2c4f5dcfd0474e5c525f1e Mon Sep 17 00:00:00 2001 From: Meysam Hadeli <35596795+meysamhadeli@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:59:45 +0330 Subject: [PATCH 2/2] refactor: refactor analyzer configs and add formating to cli --- .../Booking/src/Booking/Booking/Models/Booking.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Services/Booking/src/Booking/Booking/Models/Booking.cs b/src/Services/Booking/src/Booking/Booking/Models/Booking.cs index a895426f..354f7431 100644 --- a/src/Services/Booking/src/Booking/Booking/Models/Booking.cs +++ b/src/Services/Booking/src/Booking/Booking/Models/Booking.cs @@ -32,10 +32,10 @@ public override void When(object @event) switch (@event) { case BookingCreatedDomainEvent bookingCreated: - { - Apply(bookingCreated); - return; - } + { + Apply(bookingCreated); + return; + } } }